From 022c3596c9efae3dda87d3e07373a3b6cce20e1d Mon Sep 17 00:00:00 2001 From: bntvllnt <32437578+bntvllnt@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:44:16 +0200 Subject: [PATCH] fix: ship the Claude plugin icon as PNG (0.1.0-canary.6) The marketplace rejected .claude-plugin/icon.svg as ICON_INVALID because the generator embedded the PNG logo as a data-URI image inside the SVG. Ship assets/vllnt-logo.png as .claude-plugin/icon.png and name it with `icon` in plugin.json. Claude-Session: https://claude.ai/code/session_01VhX66NukkK9PC2hT9Zp8h1 --- CHANGELOG.md | 4 ++++ README.md | 4 ++-- VERSION | 2 +- plugins/claude/.claude-plugin/icon.png | Bin 0 -> 21408 bytes plugins/claude/.claude-plugin/icon.svg | 1 - plugins/claude/.claude-plugin/plugin.json | 3 ++- plugins/claude/SOURCE.json | 4 ++-- plugins/codex/SOURCE.json | 2 +- plugins/codex/plugin.json | 2 +- plugins/cursor/.cursor-plugin/plugin.json | 2 +- plugins/cursor/SOURCE.json | 2 +- scripts/sync.py | 19 +++++++++---------- tests/test_sync.py | 15 +++++++++------ 13 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 plugins/claude/.claude-plugin/icon.png delete mode 100644 plugins/claude/.claude-plugin/icon.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index c261c09..4ee3901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 0.1.0-canary.6 + +- Fix the Claude plugin icon being rejected as `ICON_INVALID`: ship the vllnt logo as `.claude-plugin/icon.png`, named by `icon` in `plugin.json`, instead of an SVG that embedded the PNG. + - Remove the previous marketplace name from the README; the migration note stays in this changelog. ## 0.1.0-canary.5 diff --git a/README.md b/README.md index ae20711..0195ae8 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Then start a new chat and invoke a namespaced skill such as `/vstack:plan-work`. | `upstream.lock.json` | Trusted upstream repository and immutable commit | | `VERSION` | Shared distribution version | | `scripts/sync.py` | Standard-library generator and read-only drift check | -| `assets/vllnt-logo.png` | vllnt logo; the generator wraps it as the Claude package's `.claude-plugin/icon.svg` | +| `assets/vllnt-logo.png` | vllnt logo; the generator ships it as the Claude package's `.claude-plugin/icon.png`, named by `icon` in `plugin.json` | | `plugins/claude/` | Generated Claude Code native plugin | | `plugins/codex/` | Generated portable Agent Plugin for Codex | | `plugins/cursor/` | Generated Cursor native plugin and principles rule | @@ -149,7 +149,7 @@ Automatic repository synchronization was independently verified on 2026-09-21. A Live testing caught a generated-help false hold and post-push PR-head propagation timing; fixes were reviewed and merged through PRs #5 and #6, with 37 regression tests passing. The fresh PR #7 case required neither manual metadata repair nor manual merge. Daily fallback is configured; its timer was not waited for. Push delivery, manual resends, automatic merging, and manual no-op execution were observed. -Claude Code 2.1.283 (2026-09-27): `claude plugin validate` passes for the catalog and plugin; a local-directory marketplace install into an isolated configuration installed the `vstack` plugin with all 22 skills, and a maintainer session with the local install listed every `vstack:*` skill. After `vllnt/stack` became public (merge `c3ad08c08fe460ed4f2e1a40630afe4001ddd23c`), an anonymous clone succeeded, and the CLI equivalents `claude plugin marketplace add vllnt/stack` plus the matching `claude plugin install` command in a clean, credential-free configuration installed the plugin from the GitHub source; a headless session there listed exactly the 22 `vstack:*` skills. The same commands after merge `915a4d0d519d726f84185ec698b3ed3f4c925cb8` installed it again with its icon, updated description, and 22 skills. The in-chat `/plugin` forms were not run separately. The marketplace was later renamed to `vllnt`; see the changelog for migrating earlier installs. Skill invocation behavior (including `plan-work` staying read-only), automatic skill selection, and the rest of the acceptance checklist remain unverified. +Claude Code 2.1.283 (2026-09-27): `claude plugin validate` passes for the catalog and plugin; a local-directory marketplace install into an isolated configuration installed the `vstack` plugin with all 22 skills, and a maintainer session with the local install listed every `vstack:*` skill. After `vllnt/stack` became public (merge `c3ad08c08fe460ed4f2e1a40630afe4001ddd23c`), an anonymous clone succeeded, and the CLI equivalents `claude plugin marketplace add vllnt/stack` plus the matching `claude plugin install` command in a clean, credential-free configuration installed the plugin from the GitHub source; a headless session there listed exactly the 22 `vstack:*` skills. The same commands after merge `915a4d0d519d726f84185ec698b3ed3f4c925cb8` installed it again with its icon, updated description, and 22 skills. The in-chat `/plugin` forms were not run separately. The marketplace listing later rejected that SVG icon (a PNG embedded in SVG) as `ICON_INVALID`; the replacement `.claude-plugin/icon.png` passes `claude plugin validate --strict`, but its marketplace acceptance and rendering remain unverified. The marketplace was later renamed to `vllnt`; see the changelog for migrating earlier installs. Skill invocation behavior (including `plan-work` staying read-only), automatic skill selection, and the rest of the acceptance checklist remain unverified. **Codex and Cursor installation, and startup/context-loss behavior in every host, remain unverified.** The repository is public and installable as a GitHub marketplace. A Claude plugin directory submission is in validation and is not listed yet. Canary builds are published as [GitHub prereleases](https://github.com/vllnt/stack/releases); releases are created manually. No automatic Claude/Codex principle loading exists. diff --git a/VERSION b/VERSION index 20eaaf3..8a85b8d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0-canary.5 +0.1.0-canary.6 diff --git a/plugins/claude/.claude-plugin/icon.png b/plugins/claude/.claude-plugin/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..10b7149ac2eca1c97c8c58f9200d8cd92b74b117 GIT binary patch literal 21408 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�|7>f2dxAMGv%zFOA3eZmF#D@Lac`1|zGng^sb%ckwoI;T&2(PqIqJYP8 z0Yfy7j*40>U^fs@YCHs&5Ua2O6j>wCY3SM6$DxmYPLKevxfiBV%koig%E`%DsQ6S` za2^4}`wdzCgoTBL6UG%tyRC+XSeGH#aCi<#@kel=HR8CPcG79fKoUMVf1y=ZGqHKt z=`!T7vhTd$#2HIVN9O>1MxO2!r3|eH#*wcCFMZ3hayv5deqi~lBn&2NSb!Ls6={4Q zc?6s|pGXNH?BD6y$wV$En=L3TJzy)f_6W9$!pCEbNld(0P;Q5276(R3A!xB*-vLga zg*(XsM-QKq6XsTopd1ike6v{)O#t?*uWM*%xS`w*^VxB5(=kA}*1W^+91WGVGHo2t zDQB%_Zwc1XwFYkn$Vh4g8$mj@97Yk0A&$4Y3&0QEWJc z--uB0?*Zi~4sHZ9)BruBbN708d)ULp#l?N&P*$cwU4RQ!+raagtajmc>J*Kdb*}Eg zBK-5(!UvaiAFV7Z!Ld7lPx2G0TIGUM)ntLvR?u`wjhM>tXF~%Y6~#2NOoM}!L5c?N zq+8@DsOBeEJUz9qzIsJ}<;jy6l>7I^QEnmsj*5+P)e8;fN(1Wu{`dbMx8(w!zj=9C Zmpp-H{YrWQo@0q3A|Uyu@U^zr{{fu8zXt#S literal 0 HcmV?d00001 diff --git a/plugins/claude/.claude-plugin/icon.svg b/plugins/claude/.claude-plugin/icon.svg deleted file mode 100644 index e112555..0000000 --- a/plugins/claude/.claude-plugin/icon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plugins/claude/.claude-plugin/plugin.json b/plugins/claude/.claude-plugin/plugin.json index f7edd43..598afe1 100644 --- a/plugins/claude/.claude-plugin/plugin.json +++ b/plugins/claude/.claude-plugin/plugin.json @@ -4,6 +4,7 @@ }, "description": "Evidence-led engineering workflows to plan, build, review, and ship software with AI agents. Part of the vllnt universe: open, sovereign tools for freedom by design (vllnt.com).", "homepage": "https://vllnt.com", + "icon": "./.claude-plugin/icon.png", "keywords": [ "workflows", "engineering", @@ -13,5 +14,5 @@ "license": "MIT", "name": "vstack", "repository": "https://github.com/vllnt/stack", - "version": "0.1.0-canary.5" + "version": "0.1.0-canary.6" } diff --git a/plugins/claude/SOURCE.json b/plugins/claude/SOURCE.json index ba64368..9368076 100644 --- a/plugins/claude/SOURCE.json +++ b/plugins/claude/SOURCE.json @@ -1,7 +1,7 @@ { "files": { - ".claude-plugin/icon.svg": "0b4b639f6df841b4904ec0cd9f6d38131c6373ce4a52a1e7fe6452a37598d7f1", - ".claude-plugin/plugin.json": "075f3e05fe3733d2930d0aaaf378d02758436dfa4f4006c34eda3c505b48e1ef", + ".claude-plugin/icon.png": "71502ebbc531cc46866dffe33dae3207ad22cf529f801ec971bb69114c064608", + ".claude-plugin/plugin.json": "60311ca1334975967e4ae9ae7710b7b57a9a854f35b5fff3b8da26a008e8c63b", "LICENSE": "24ba061a4603738ddd5105ef4abc198eeded5ac7efce23b360561816f4dd6559", "skills/build-landing-page/README.md": "46a280eadac8962180cf3f0c42eb0e845a3b0156a644569182d932881e1d51f8", "skills/build-landing-page/SKILL.md": "b08e4963d6149f3647b28a0d9dccdc8793812abab95a1d081d93699e142de0f0", diff --git a/plugins/codex/SOURCE.json b/plugins/codex/SOURCE.json index 4c34085..3513f3a 100644 --- a/plugins/codex/SOURCE.json +++ b/plugins/codex/SOURCE.json @@ -1,7 +1,7 @@ { "files": { "LICENSE": "24ba061a4603738ddd5105ef4abc198eeded5ac7efce23b360561816f4dd6559", - "plugin.json": "c78e76ede21d79745dc7572f7ba3f563d9296d9d1c3404cedc217f96597f320b", + "plugin.json": "1ba701827e2e216e367635d59628b4e5e6d599090dc2ea73bd0a2afe996aff2b", "skills/build-landing-page/README.md": "46a280eadac8962180cf3f0c42eb0e845a3b0156a644569182d932881e1d51f8", "skills/build-landing-page/SKILL.md": "b08e4963d6149f3647b28a0d9dccdc8793812abab95a1d081d93699e142de0f0", "skills/build-landing-page/references/copywriting-formulas.md": "6d19f6166e1badcfdda0af4f8b303160e94ee6e6149321157e12849654cc013e", diff --git a/plugins/codex/plugin.json b/plugins/codex/plugin.json index c7cc4fd..499ffaf 100644 --- a/plugins/codex/plugin.json +++ b/plugins/codex/plugin.json @@ -6,5 +6,5 @@ "description": "Evidence-led engineering workflows to plan, build, review, and ship software with AI agents. Part of the vllnt universe: open, sovereign tools for freedom by design (vllnt.com).", "license": "MIT", "name": "vstack", - "version": "0.1.0-canary.5" + "version": "0.1.0-canary.6" } diff --git a/plugins/cursor/.cursor-plugin/plugin.json b/plugins/cursor/.cursor-plugin/plugin.json index 3f06dc7..72b4293 100644 --- a/plugins/cursor/.cursor-plugin/plugin.json +++ b/plugins/cursor/.cursor-plugin/plugin.json @@ -5,5 +5,5 @@ "description": "Evidence-led engineering workflows to plan, build, review, and ship software with AI agents. Part of the vllnt universe: open, sovereign tools for freedom by design (vllnt.com).", "license": "MIT", "name": "vstack", - "version": "0.1.0-canary.5" + "version": "0.1.0-canary.6" } diff --git a/plugins/cursor/SOURCE.json b/plugins/cursor/SOURCE.json index c70f232..036df41 100644 --- a/plugins/cursor/SOURCE.json +++ b/plugins/cursor/SOURCE.json @@ -1,6 +1,6 @@ { "files": { - ".cursor-plugin/plugin.json": "63c0531e7ec6ff67f7fecd54ee886f3d10706dbab51820807b373378ce7ee4b3", + ".cursor-plugin/plugin.json": "b8908a118e691f11f901524b4f28b372cf35699b60392575c2a4a5dfaf3923a9", "LICENSE": "24ba061a4603738ddd5105ef4abc198eeded5ac7efce23b360561816f4dd6559", "rules/vstack-principles.mdc": "5234bc0506e0a328d6163cd83785d2f37dd1fc23ef84287d71e03b92459e064a", "skills/build-landing-page/README.md": "46a280eadac8962180cf3f0c42eb0e845a3b0156a644569182d932881e1d51f8", diff --git a/scripts/sync.py b/scripts/sync.py index 6d52e6e..d098b54 100644 --- a/scripts/sync.py +++ b/scripts/sync.py @@ -3,7 +3,6 @@ from __future__ import annotations import argparse -import base64 import gzip import hashlib import io @@ -220,17 +219,16 @@ def payload(files: dict[str, bytes]) -> tuple[dict[str, bytes], bytes]: return skills, rule.encode() -def icon_svg(png: bytes) -> bytes: - """Wrap a square PNG in the SVG file Claude's plugin directory looks for.""" - if png[:8] != b"\x89PNG\r\n\x1a\n": +def icon_png(png: bytes) -> bytes: + """Check the PNG shipped as Claude's plugin icon; listings reject SVGs with embedded images.""" + if png[:8] != b"\x89PNG\r\n\x1a\n" or png[12:16] != b"IHDR": raise Invalid("Claude icon must be a PNG") width, height = struct.unpack(">II", png[16:24]) if width != height or width < 128: raise Invalid("Claude icon must be square and at least 128px") - data = base64.b64encode(png).decode() - return (f'\n').encode() + if len(png) > MAX_FILE: + raise Invalid("Claude icon must be at most 2 MiB") + return png def build(files: dict[str, bytes], lock: dict, version: str) -> dict[str, bytes]: @@ -244,14 +242,15 @@ def build(files: dict[str, bytes], lock: dict, version: str) -> dict[str, bytes] "author": {"name": "vllnt"}, "license": "MIT"} if host == "claude": manifest.update({"homepage": HOMEPAGE, "repository": REPOSITORY, - "keywords": ["workflows", "engineering", "principles", "skills"]}) + "keywords": ["workflows", "engineering", "principles", "skills"], + "icon": "./.claude-plugin/icon.png"}) if host == "codex": manifest["$schema"] = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json" manifest_path = {"claude": ".claude-plugin/plugin.json", "codex": "plugin.json", "cursor": ".cursor-plugin/plugin.json"}[host] package = {**skills, "LICENSE": files["LICENSE"], manifest_path: encoded(manifest)} if host == "claude": - package[".claude-plugin/icon.svg"] = icon_svg(CLAUDE_ICON.read_bytes()) + package[".claude-plugin/icon.png"] = icon_png(CLAUDE_ICON.read_bytes()) if host == "cursor": package["rules/vstack-principles.mdc"] = rule package["SOURCE.json"] = encoded({"upstream": lock, "files": { diff --git a/tests/test_sync.py b/tests/test_sync.py index 4257f1c..8ad87f5 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -54,8 +54,9 @@ def test_deterministic_standalone_packages_and_provenance(self): self.assertEqual(package["skills/test-work/SKILL.md"], files["workflows/test-work/SKILL.md"]) self.assertEqual("rules/vstack-principles.mdc" in package, host == "cursor") self.assertEqual(package["LICENSE"], files["LICENSE"]) - self.assertEqual(".claude-plugin/icon.svg" in package, host == "claude") - self.assertIn(b"data:image/png;base64,", first["plugins/claude/.claude-plugin/icon.svg"]) + self.assertEqual(".claude-plugin/icon.png" in package, host == "claude") + self.assertNotIn(".claude-plugin/icon.svg", package) + self.assertEqual(first["plugins/claude/.claude-plugin/icon.png"], sync.CLAUDE_ICON.read_bytes()) claude = json.loads(first[".claude-plugin/marketplace.json"]) codex = json.loads(first[".agents/plugins/marketplace.json"]) cursor = json.loads(first[".cursor-plugin/marketplace.json"]) @@ -70,15 +71,17 @@ def test_deterministic_standalone_packages_and_provenance(self): ("cursor", "plugins/cursor/.cursor-plugin/plugin.json"))} self.assertEqual(manifests["claude"]["repository"], "https://github.com/vllnt/stack") self.assertEqual(manifests["claude"]["homepage"], "https://vllnt.com") + self.assertEqual(manifests["claude"]["icon"], "./.claude-plugin/icon.png") + self.assertNotIn("icon", manifests["codex"]) self.assertNotIn("repository", manifests["codex"]) self.assertNotIn("repository", manifests["cursor"]) - def test_claude_icon_requires_square_png(self): + def test_claude_icon_requires_small_square_png(self): png = lambda w, h: b"\x89PNG\r\n\x1a\n" + b"\0\0\0\rIHDR" + struct.pack(">II", w, h) - self.assertIn(b'viewBox="0 0 256 256"', sync.icon_svg(png(256, 256))) - for bad in (png(256, 128), png(64, 64), b"" + bytes(32)): + self.assertEqual(sync.icon_png(png(256, 256)), png(256, 256)) + for bad in (png(256, 128), png(64, 64), b"" + bytes(32), png(256, 256) + bytes(sync.MAX_FILE)): with self.assertRaises(sync.Invalid): - sync.icon_svg(bad) + sync.icon_png(bad) def test_added_and_removed_workflows(self): files = source()