fix: use object-shaped author field and surface descriptions in marketplace.json #8
@@ -8,6 +8,10 @@
|
|||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "caveman",
|
"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": {
|
||||||
"source": "github",
|
"source": "github",
|
||||||
"repo": "JuliusBrussee/caveman"
|
"repo": "JuliusBrussee/caveman"
|
||||||
@@ -15,30 +19,58 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "core-essentials",
|
"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"
|
"source": "./dist/claude-code/core-essentials"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "date-time",
|
"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"
|
"source": "./dist/claude-code/date-time"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "docs-sync-audit",
|
"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"
|
"source": "./dist/claude-code/docs-sync-audit"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "git-publish-workflow",
|
"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"
|
"source": "./dist/claude-code/git-publish-workflow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "gitea-mcp",
|
"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"
|
"source": "./dist/claude-code/gitea-mcp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "github-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"
|
"source": "./dist/claude-code/github-mcp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mattpocock-skills",
|
"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": {
|
||||||
"source": "github",
|
"source": "github",
|
||||||
"repo": "mattpocock/skills"
|
"repo": "mattpocock/skills"
|
||||||
@@ -46,10 +78,18 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mirror-it",
|
"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"
|
"source": "./dist/claude-code/mirror-it"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ponytail",
|
"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": {
|
||||||
"source": "github",
|
"source": "github",
|
||||||
"repo": "DietrichGebert/ponytail"
|
"repo": "DietrichGebert/ponytail"
|
||||||
@@ -57,22 +97,39 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "readme-sync-audit",
|
"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"
|
"source": "./dist/claude-code/readme-sync-audit"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ship-it",
|
"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"
|
"source": "./dist/claude-code/ship-it"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "systematic-enumeration",
|
"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"
|
"source": "./dist/claude-code/systematic-enumeration"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "technical-devlog-scribe",
|
"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"
|
"source": "./dist/claude-code/technical-devlog-scribe"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "rootiest-ai-all",
|
"name": "rootiest-ai-all",
|
||||||
|
"description": "Reusable AI skills for Claude Code and Antigravity CLI (all skills)",
|
||||||
"source": "./dist/claude-code/rootiest-ai-all"
|
"source": "./dist/claude-code/rootiest-ai-all"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
"name": "core-essentials",
|
"name": "core-essentials",
|
||||||
"description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.",
|
"description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -2,5 +2,7 @@
|
|||||||
"name": "date-time",
|
"name": "date-time",
|
||||||
"description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.",
|
"description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
"name": "docs-sync-audit",
|
"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.",
|
"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",
|
"version": "1.3.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
"name": "git-publish-workflow",
|
"name": "git-publish-workflow",
|
||||||
"description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.",
|
"description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.",
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -2,5 +2,7 @@
|
|||||||
"name": "gitea-mcp",
|
"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.",
|
"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",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -2,5 +2,7 @@
|
|||||||
"name": "github-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.",
|
"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",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -2,5 +2,7 @@
|
|||||||
"name": "mirror-it",
|
"name": "mirror-it",
|
||||||
"description": "Configures a Gitea repository to automatically push-mirror its contents to GitHub using the tea CLI and jq.",
|
"description": "Configures a Gitea repository to automatically push-mirror its contents to GitHub using the tea CLI and jq.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
"name": "readme-sync-audit",
|
"name": "readme-sync-audit",
|
||||||
"description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.",
|
"description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.",
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -2,5 +2,7 @@
|
|||||||
"name": "ship-it",
|
"name": "ship-it",
|
||||||
"description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.",
|
"description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
"name": "systematic-enumeration",
|
"name": "systematic-enumeration",
|
||||||
"description": "Forces element-by-element verification for finite sets to prevent counting errors.",
|
"description": "Forces element-by-element verification for finite sets to prevent counting errors.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
"name": "technical-devlog-scribe",
|
"name": "technical-devlog-scribe",
|
||||||
"description": "Generates a highly structured, objective technical summary of a development session.",
|
"description": "Generates a highly structured, objective technical summary of a development session.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": {
|
||||||
|
"name": "Rootiest"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "caveman",
|
"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.",
|
"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",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest",
|
"author": { "name": "Rootiest" },
|
||||||
"targets": ["claude-code"],
|
"targets": ["claude-code"],
|
||||||
"external": {
|
"external": {
|
||||||
"claude-code": {
|
"claude-code": {
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "core-essentials",
|
"name": "core-essentials",
|
||||||
"description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.",
|
"description": "Core cross-tool utilities, including delegation to the Antigravity CLI (agy) for grounded research and large multi-file audits.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "date-time",
|
"name": "date-time",
|
||||||
"description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.",
|
"description": "Retrieves the exact current date and time for time-sensitive queries, scheduling, duration calculations, and validating chronological context.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "docs-sync-audit",
|
"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.",
|
"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",
|
"version": "1.3.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "git-publish-workflow",
|
"name": "git-publish-workflow",
|
||||||
"description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.",
|
"description": "Automates branching, conventional commits, testing, and PR creation for uncommitted or staged work. Handles both independent and stacked PRs.",
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "gitea-mcp",
|
"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.",
|
"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",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "github-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.",
|
"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",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "mattpocock-skills",
|
"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.",
|
"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",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest",
|
"author": { "name": "Rootiest" },
|
||||||
"targets": ["claude-code"],
|
"targets": ["claude-code"],
|
||||||
"external": {
|
"external": {
|
||||||
"claude-code": {
|
"claude-code": {
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "mirror-it",
|
"name": "mirror-it",
|
||||||
"description": "Configures a Gitea repository to automatically push-mirror its contents to GitHub using the tea CLI and jq.",
|
"description": "Configures a Gitea repository to automatically push-mirror its contents to GitHub using the tea CLI and jq.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "ponytail",
|
"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.",
|
"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",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest",
|
"author": { "name": "Rootiest" },
|
||||||
"targets": ["claude-code"],
|
"targets": ["claude-code"],
|
||||||
"external": {
|
"external": {
|
||||||
"claude-code": {
|
"claude-code": {
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "readme-sync-audit",
|
"name": "readme-sync-audit",
|
||||||
"description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.",
|
"description": "Analyzes repository delta since the last documentation update and synchronizes the README.md with the current codebase state.",
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "ship-it",
|
"name": "ship-it",
|
||||||
"description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.",
|
"description": "Runs a comprehensive pre-flight audit, syncs the README, and publishes the changes to a new PR.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "systematic-enumeration",
|
"name": "systematic-enumeration",
|
||||||
"description": "Forces element-by-element verification for finite sets to prevent counting errors.",
|
"description": "Forces element-by-element verification for finite sets to prevent counting errors.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
"name": "technical-devlog-scribe",
|
"name": "technical-devlog-scribe",
|
||||||
"description": "Generates a highly structured, objective technical summary of a development session.",
|
"description": "Generates a highly structured, objective technical summary of a development session.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Rootiest"
|
"author": { "name": "Rootiest" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,6 +159,17 @@ def validate_targets(value: "list | None", valid_targets: list[str], context: st
|
|||||||
return value
|
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":
|
def validate_external(value: "dict | None", valid_targets: list[str], context: str) -> "dict | None":
|
||||||
"""Validate an optional 'external' map of target -> marketplace source object."""
|
"""Validate an optional 'external' map of target -> marketplace source object."""
|
||||||
if value is None:
|
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:
|
for field in REQUIRED_PLUGIN_FIELDS:
|
||||||
if not meta.get(field):
|
if not meta.get(field):
|
||||||
raise ValidationError(f"{manifest_path}: missing required field '{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_targets = validate_targets(meta.get("targets"), valid_targets, str(manifest_path))
|
||||||
plugin_external = validate_external(meta.get("external"), valid_targets, str(manifest_path))
|
plugin_external = validate_external(meta.get("external"), valid_targets, str(manifest_path))
|
||||||
if plugin_external is not None:
|
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)
|
shutil.rmtree(plugins_out)
|
||||||
plugins_out.mkdir(parents=True)
|
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 = []
|
marketplace_entries = []
|
||||||
for p in plugins:
|
for p in plugins:
|
||||||
name = p["meta"]["name"]
|
name = p["meta"]["name"]
|
||||||
external_source = (p["external"] or {}).get("claude-code") if p["external"] else None
|
external_source = (p["external"] or {}).get("claude-code") if p["external"] else None
|
||||||
if external_source is not None:
|
if external_source is not None:
|
||||||
if plugin_supports(p, "claude-code"):
|
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
|
continue
|
||||||
if write_claude_plugin(p, plugins_out / name):
|
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_id = manifest["bundle"]["id"]
|
||||||
bundle_skills = [(p, s) for p in plugins for s in active_skills(p, "claude-code")]
|
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"
|
bundle_skills_dir = bundle_dest / "skills"
|
||||||
for _, s in bundle_skills:
|
for _, s in bundle_skills:
|
||||||
shutil.copytree(s["dir"], bundle_skills_dir / s["frontmatter"]["name"])
|
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 = {
|
marketplace = {
|
||||||
"name": manifest["marketplace"]["name"],
|
"name": manifest["marketplace"]["name"],
|
||||||
|
|||||||
Reference in New Issue
Block a user