From 8400f41419527a78194198176ab0ba3970769f5b Mon Sep 17 00:00:00 2001 From: Rootiest Date: Tue, 25 Aug 2026 00:15:31 -0400 Subject: [PATCH] fix: use object-shaped author field and surface descriptions in marketplace.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code's plugin/marketplace schemas require author to be an object ({"name": ...}), not a bare string — installing any plugin from this marketplace failed validation ("author: Invalid input: expected object, received string"). Convert every plugins/*/plugin.json author field accordingly, and add a build-time check so this can't regress. Also propagate each plugin's description/author into the generated marketplace.json entries (not just its own nested plugin.json), matching Anthropic's official marketplace shape, so the Discover UI shows a description before install instead of a blank "Plugin details" pane. --- .claude-plugin/marketplace.json | 57 +++++++++++++++++++ .../.claude-plugin/plugin.json | 4 +- .../date-time/.claude-plugin/plugin.json | 4 +- .../.claude-plugin/plugin.json | 4 +- .../.claude-plugin/plugin.json | 4 +- .../gitea-mcp/.claude-plugin/plugin.json | 4 +- .../github-mcp/.claude-plugin/plugin.json | 4 +- .../mirror-it/.claude-plugin/plugin.json | 4 +- .../.claude-plugin/plugin.json | 4 +- .../ship-it/.claude-plugin/plugin.json | 4 +- .../.claude-plugin/plugin.json | 4 +- .../.claude-plugin/plugin.json | 4 +- plugins/caveman/plugin.json | 2 +- plugins/core-essentials/plugin.json | 2 +- plugins/date-time/plugin.json | 2 +- plugins/docs-sync-audit/plugin.json | 2 +- plugins/git-publish-workflow/plugin.json | 2 +- plugins/gitea-mcp/plugin.json | 2 +- plugins/github-mcp/plugin.json | 2 +- plugins/mattpocock-skills/plugin.json | 2 +- plugins/mirror-it/plugin.json | 2 +- plugins/ponytail/plugin.json | 2 +- plugins/readme-sync-audit/plugin.json | 2 +- plugins/ship-it/plugin.json | 2 +- plugins/systematic-enumeration/plugin.json | 2 +- plugins/technical-devlog-scribe/plugin.json | 2 +- scripts/generate_plugins.py | 33 ++++++++++- 27 files changed, 134 insertions(+), 28 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 46890af..e30a954 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,6 +8,10 @@ "plugins": [ { "name": "caveman", + "description": "Ultra-compressed communication mode from JuliusBrussee/caveman \u2014 cuts output tokens while keeping full technical accuracy. Installed straight from the upstream repo, not vendored here.", + "author": { + "name": "Rootiest" + }, "source": { "source": "github", "repo": "JuliusBrussee/caveman" @@ -15,30 +19,58 @@ }, { "name": "core-essentials", + "description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/core-essentials" }, { "name": "date-time", + "description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/date-time" }, { "name": "docs-sync-audit", + "description": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/docs-sync-audit" }, { "name": "git-publish-workflow", + "description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/git-publish-workflow" }, { "name": "gitea-mcp", + "description": "Connects Claude Code/agy to a Gitea instance via the official gitea-mcp server, authenticated with GITEA_HOST/GITEA_ACCESS_TOKEN you set locally.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/gitea-mcp" }, { "name": "github-mcp", + "description": "Connects Claude Code/agy to GitHub via the official MCP server, authenticated with a GITHUB_PERSONAL_ACCESS_TOKEN you set locally.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/github-mcp" }, { "name": "mattpocock-skills", + "description": "Matt Pocock's agent skills for real engineering from mattpocock/skills \u2014 grilling, spec/ticket flows, TDD, code review, domain modelling and more. Installed straight from the upstream repo, not vendored here.", + "author": { + "name": "Rootiest" + }, "source": { "source": "github", "repo": "mattpocock/skills" @@ -46,10 +78,18 @@ }, { "name": "mirror-it", + "description": "Configures a Gitea repository to automatically push-mirror its contents to GitHub using the tea CLI and jq.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/mirror-it" }, { "name": "ponytail", + "description": "Lazy senior dev mode from DietrichGebert/ponytail \u2014 forces the simplest, shortest solution that actually works (YAGNI, stdlib first, no unrequested abstractions). Installed straight from the upstream repo, not vendored here.", + "author": { + "name": "Rootiest" + }, "source": { "source": "github", "repo": "DietrichGebert/ponytail" @@ -57,22 +97,39 @@ }, { "name": "readme-sync-audit", + "description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/readme-sync-audit" }, { "name": "ship-it", + "description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/ship-it" }, { "name": "systematic-enumeration", + "description": "Forces element-by-element verification for finite sets to prevent counting errors.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/systematic-enumeration" }, { "name": "technical-devlog-scribe", + "description": "Generates a highly structured, objective technical summary of a development session.", + "author": { + "name": "Rootiest" + }, "source": "./dist/claude-code/technical-devlog-scribe" }, { "name": "rootiest-ai-all", + "description": "Reusable AI skills for Claude Code and Antigravity CLI (all skills)", "source": "./dist/claude-code/rootiest-ai-all" } ] diff --git a/dist/claude-code/core-essentials/.claude-plugin/plugin.json b/dist/claude-code/core-essentials/.claude-plugin/plugin.json index e9165e1..1d99eb6 100644 --- a/dist/claude-code/core-essentials/.claude-plugin/plugin.json +++ b/dist/claude-code/core-essentials/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "core-essentials", "description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/date-time/.claude-plugin/plugin.json b/dist/claude-code/date-time/.claude-plugin/plugin.json index 9910091..fdd1fc0 100644 --- a/dist/claude-code/date-time/.claude-plugin/plugin.json +++ b/dist/claude-code/date-time/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "date-time", "description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/docs-sync-audit/.claude-plugin/plugin.json b/dist/claude-code/docs-sync-audit/.claude-plugin/plugin.json index 7b1f864..7ebbfc6 100644 --- a/dist/claude-code/docs-sync-audit/.claude-plugin/plugin.json +++ b/dist/claude-code/docs-sync-audit/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "docs-sync-audit", "description": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state.", "version": "1.3.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/git-publish-workflow/.claude-plugin/plugin.json b/dist/claude-code/git-publish-workflow/.claude-plugin/plugin.json index 128244a..482455e 100644 --- a/dist/claude-code/git-publish-workflow/.claude-plugin/plugin.json +++ b/dist/claude-code/git-publish-workflow/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "git-publish-workflow", "description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.", "version": "1.2.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/gitea-mcp/.claude-plugin/plugin.json b/dist/claude-code/gitea-mcp/.claude-plugin/plugin.json index c834f46..4285e72 100644 --- a/dist/claude-code/gitea-mcp/.claude-plugin/plugin.json +++ b/dist/claude-code/gitea-mcp/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "gitea-mcp", "description": "Connects Claude Code/agy to a Gitea instance via the official gitea-mcp server, authenticated with GITEA_HOST/GITEA_ACCESS_TOKEN you set locally.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/github-mcp/.claude-plugin/plugin.json b/dist/claude-code/github-mcp/.claude-plugin/plugin.json index e8fb56d..1f2c7c5 100644 --- a/dist/claude-code/github-mcp/.claude-plugin/plugin.json +++ b/dist/claude-code/github-mcp/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "github-mcp", "description": "Connects Claude Code/agy to GitHub via the official MCP server, authenticated with a GITHUB_PERSONAL_ACCESS_TOKEN you set locally.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/mirror-it/.claude-plugin/plugin.json b/dist/claude-code/mirror-it/.claude-plugin/plugin.json index 4515e22..77412d0 100644 --- a/dist/claude-code/mirror-it/.claude-plugin/plugin.json +++ b/dist/claude-code/mirror-it/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "mirror-it", "description": "Configures a Gitea repository to automatically push-mirror its contents to GitHub using the tea CLI and jq.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/readme-sync-audit/.claude-plugin/plugin.json b/dist/claude-code/readme-sync-audit/.claude-plugin/plugin.json index 1429967..c31828d 100644 --- a/dist/claude-code/readme-sync-audit/.claude-plugin/plugin.json +++ b/dist/claude-code/readme-sync-audit/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "readme-sync-audit", "description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.", "version": "1.2.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/ship-it/.claude-plugin/plugin.json b/dist/claude-code/ship-it/.claude-plugin/plugin.json index 356d960..d9d37f9 100644 --- a/dist/claude-code/ship-it/.claude-plugin/plugin.json +++ b/dist/claude-code/ship-it/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "ship-it", "description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/systematic-enumeration/.claude-plugin/plugin.json b/dist/claude-code/systematic-enumeration/.claude-plugin/plugin.json index aaf1e32..5e2bca7 100644 --- a/dist/claude-code/systematic-enumeration/.claude-plugin/plugin.json +++ b/dist/claude-code/systematic-enumeration/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "systematic-enumeration", "description": "Forces element-by-element verification for finite sets to prevent counting errors.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/dist/claude-code/technical-devlog-scribe/.claude-plugin/plugin.json b/dist/claude-code/technical-devlog-scribe/.claude-plugin/plugin.json index 3171855..7e3d19d 100644 --- a/dist/claude-code/technical-devlog-scribe/.claude-plugin/plugin.json +++ b/dist/claude-code/technical-devlog-scribe/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "technical-devlog-scribe", "description": "Generates a highly structured, objective technical summary of a development session.", "version": "1.0.0", - "author": "Rootiest" + "author": { + "name": "Rootiest" + } } diff --git a/plugins/caveman/plugin.json b/plugins/caveman/plugin.json index 87d0ff6..cf4a629 100644 --- a/plugins/caveman/plugin.json +++ b/plugins/caveman/plugin.json @@ -2,7 +2,7 @@ "name": "caveman", "description": "Ultra-compressed communication mode from JuliusBrussee/caveman — cuts output tokens while keeping full technical accuracy. Installed straight from the upstream repo, not vendored here.", "version": "1.0.0", - "author": "Rootiest", + "author": { "name": "Rootiest" }, "targets": ["claude-code"], "external": { "claude-code": { diff --git a/plugins/core-essentials/plugin.json b/plugins/core-essentials/plugin.json index e9165e1..aeb104f 100644 --- a/plugins/core-essentials/plugin.json +++ b/plugins/core-essentials/plugin.json @@ -2,5 +2,5 @@ "name": "core-essentials", "description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/date-time/plugin.json b/plugins/date-time/plugin.json index 9910091..284d56a 100644 --- a/plugins/date-time/plugin.json +++ b/plugins/date-time/plugin.json @@ -2,5 +2,5 @@ "name": "date-time", "description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/docs-sync-audit/plugin.json b/plugins/docs-sync-audit/plugin.json index 7b1f864..28562d9 100644 --- a/plugins/docs-sync-audit/plugin.json +++ b/plugins/docs-sync-audit/plugin.json @@ -2,5 +2,5 @@ "name": "docs-sync-audit", "description": "Analyzes repository delta since the last documentation update and synchronizes the Single Source of Truth (docs/, wiki, or README) with the current codebase state.", "version": "1.3.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/git-publish-workflow/plugin.json b/plugins/git-publish-workflow/plugin.json index 128244a..ef62097 100644 --- a/plugins/git-publish-workflow/plugin.json +++ b/plugins/git-publish-workflow/plugin.json @@ -2,5 +2,5 @@ "name": "git-publish-workflow", "description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.", "version": "1.2.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/gitea-mcp/plugin.json b/plugins/gitea-mcp/plugin.json index c834f46..b8d245b 100644 --- a/plugins/gitea-mcp/plugin.json +++ b/plugins/gitea-mcp/plugin.json @@ -2,5 +2,5 @@ "name": "gitea-mcp", "description": "Connects Claude Code/agy to a Gitea instance via the official gitea-mcp server, authenticated with GITEA_HOST/GITEA_ACCESS_TOKEN you set locally.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/github-mcp/plugin.json b/plugins/github-mcp/plugin.json index e8fb56d..78ff518 100644 --- a/plugins/github-mcp/plugin.json +++ b/plugins/github-mcp/plugin.json @@ -2,5 +2,5 @@ "name": "github-mcp", "description": "Connects Claude Code/agy to GitHub via the official MCP server, authenticated with a GITHUB_PERSONAL_ACCESS_TOKEN you set locally.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/mattpocock-skills/plugin.json b/plugins/mattpocock-skills/plugin.json index ddf990d..1c496b0 100644 --- a/plugins/mattpocock-skills/plugin.json +++ b/plugins/mattpocock-skills/plugin.json @@ -2,7 +2,7 @@ "name": "mattpocock-skills", "description": "Matt Pocock's agent skills for real engineering from mattpocock/skills — grilling, spec/ticket flows, TDD, code review, domain modelling and more. Installed straight from the upstream repo, not vendored here.", "version": "1.0.0", - "author": "Rootiest", + "author": { "name": "Rootiest" }, "targets": ["claude-code"], "external": { "claude-code": { diff --git a/plugins/mirror-it/plugin.json b/plugins/mirror-it/plugin.json index 4515e22..5d9c8a0 100644 --- a/plugins/mirror-it/plugin.json +++ b/plugins/mirror-it/plugin.json @@ -2,5 +2,5 @@ "name": "mirror-it", "description": "Configures a Gitea repository to automatically push-mirror its contents to GitHub using the tea CLI and jq.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/ponytail/plugin.json b/plugins/ponytail/plugin.json index c26f548..e6b6fd1 100644 --- a/plugins/ponytail/plugin.json +++ b/plugins/ponytail/plugin.json @@ -2,7 +2,7 @@ "name": "ponytail", "description": "Lazy senior dev mode from DietrichGebert/ponytail — forces the simplest, shortest solution that actually works (YAGNI, stdlib first, no unrequested abstractions). Installed straight from the upstream repo, not vendored here.", "version": "1.0.0", - "author": "Rootiest", + "author": { "name": "Rootiest" }, "targets": ["claude-code"], "external": { "claude-code": { diff --git a/plugins/readme-sync-audit/plugin.json b/plugins/readme-sync-audit/plugin.json index 1429967..a03b648 100644 --- a/plugins/readme-sync-audit/plugin.json +++ b/plugins/readme-sync-audit/plugin.json @@ -2,5 +2,5 @@ "name": "readme-sync-audit", "description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.", "version": "1.2.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/ship-it/plugin.json b/plugins/ship-it/plugin.json index 356d960..f74e6a7 100644 --- a/plugins/ship-it/plugin.json +++ b/plugins/ship-it/plugin.json @@ -2,5 +2,5 @@ "name": "ship-it", "description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/systematic-enumeration/plugin.json b/plugins/systematic-enumeration/plugin.json index aaf1e32..8c71964 100644 --- a/plugins/systematic-enumeration/plugin.json +++ b/plugins/systematic-enumeration/plugin.json @@ -2,5 +2,5 @@ "name": "systematic-enumeration", "description": "Forces element-by-element verification for finite sets to prevent counting errors.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/plugins/technical-devlog-scribe/plugin.json b/plugins/technical-devlog-scribe/plugin.json index 3171855..7e9dbd9 100644 --- a/plugins/technical-devlog-scribe/plugin.json +++ b/plugins/technical-devlog-scribe/plugin.json @@ -2,5 +2,5 @@ "name": "technical-devlog-scribe", "description": "Generates a highly structured, objective technical summary of a development session.", "version": "1.0.0", - "author": "Rootiest" + "author": { "name": "Rootiest" } } diff --git a/scripts/generate_plugins.py b/scripts/generate_plugins.py index 2cccb71..1db2445 100755 --- a/scripts/generate_plugins.py +++ b/scripts/generate_plugins.py @@ -159,6 +159,17 @@ def validate_targets(value: "list | None", valid_targets: list[str], context: st return value +def validate_author(value: "dict | str | None", context: str) -> None: + """Validate an optional 'author' field. The Claude Code marketplace/plugin + schemas both require an object ({"name": ...}), not a bare string.""" + if value is None: + return + if not isinstance(value, dict) or not value.get("name"): + raise ValidationError( + f'{context}: \'author\' must be an object with a \'name\' field, e.g. {{"name": "..."}}' + ) + + def validate_external(value: "dict | None", valid_targets: list[str], context: str) -> "dict | None": """Validate an optional 'external' map of target -> marketplace source object.""" if value is None: @@ -189,6 +200,7 @@ def discover_plugins(merged_root: Path, valid_targets: list[str]) -> list[dict]: for field in REQUIRED_PLUGIN_FIELDS: if not meta.get(field): raise ValidationError(f"{manifest_path}: missing required field '{field}'") + validate_author(meta.get("author"), str(manifest_path)) plugin_targets = validate_targets(meta.get("targets"), valid_targets, str(manifest_path)) plugin_external = validate_external(meta.get("external"), valid_targets, str(manifest_path)) if plugin_external is not None: @@ -399,16 +411,25 @@ def gen_claude_code(manifest: dict, plugins: list[dict], out_dir: Path) -> None: shutil.rmtree(plugins_out) plugins_out.mkdir(parents=True) + def marketplace_entry(name: str, meta: dict, source) -> dict: + entry: dict = {"name": name} + if meta.get("description"): + entry["description"] = meta["description"] + if meta.get("author"): + entry["author"] = meta["author"] + entry["source"] = source + return entry + marketplace_entries = [] for p in plugins: name = p["meta"]["name"] external_source = (p["external"] or {}).get("claude-code") if p["external"] else None if external_source is not None: if plugin_supports(p, "claude-code"): - marketplace_entries.append({"name": name, "source": external_source}) + marketplace_entries.append(marketplace_entry(name, p["meta"], external_source)) continue if write_claude_plugin(p, plugins_out / name): - marketplace_entries.append({"name": name, "source": f"./dist/claude-code/{name}"}) + marketplace_entries.append(marketplace_entry(name, p["meta"], f"./dist/claude-code/{name}")) bundle_id = manifest["bundle"]["id"] bundle_skills = [(p, s) for p in plugins for s in active_skills(p, "claude-code")] @@ -426,7 +447,13 @@ def gen_claude_code(manifest: dict, plugins: list[dict], out_dir: Path) -> None: bundle_skills_dir = bundle_dest / "skills" for _, s in bundle_skills: shutil.copytree(s["dir"], bundle_skills_dir / s["frontmatter"]["name"]) - marketplace_entries.append({"name": bundle_id, "source": f"./dist/claude-code/{bundle_id}"}) + marketplace_entries.append( + marketplace_entry( + bundle_id, + {"description": f"{manifest['marketplace']['description']} (all skills)"}, + f"./dist/claude-code/{bundle_id}", + ) + ) marketplace = { "name": manifest["marketplace"]["name"], -- 2.54.0