From d5308f1eddbd61cd1cf6adee2221f97b6f5e0cc8 Mon Sep 17 00:00:00 2001 From: Richardson Gunde Date: Sun, 26 Jul 2026 15:37:44 +0530 Subject: [PATCH 1/3] Fix domain plugin manifests: all 10 packs fail Claude Code install validation (#468) commands/skills/agents path entries were missing the required "./" prefix (Claude Code plugin manifest spec: "all paths must be relative to the plugin root and start with ./") and agents pointed at bare directories where the schema only accepts individual .md file paths. Fixed the generator and regenerated all 10 domain plugin.json manifests + marketplace.json. Live-verified: /plugin install backend (and the other 9 domain packs) now install cleanly with no manifest validation errors. --- plugins/backend/plugin.json | 58 ++++++++++++++++++--------- plugins/data-ml/plugin.json | 34 +++++++++------- plugins/devops-cloud/plugin.json | 57 ++++++++++++++++++--------- plugins/docs-content/plugin.json | 32 ++++++++++----- plugins/frontend-mobile/plugin.json | 35 +++++++++++------ plugins/languages/plugin.json | 47 +++++++++++++--------- plugins/product-team/plugin.json | 44 +++++++++++---------- plugins/quality-testing/plugin.json | 61 +++++++++++++++++------------ plugins/security/plugin.json | 16 ++++---- plugins/specialized/plugin.json | 21 +++++----- scripts/generate-domain-plugins.mjs | 27 +++++++++++-- 11 files changed, 278 insertions(+), 154 deletions(-) diff --git a/plugins/backend/plugin.json b/plugins/backend/plugin.json index 4131a3b7..62fe06bf 100644 --- a/plugins/backend/plugin.json +++ b/plugins/backend/plugin.json @@ -8,27 +8,49 @@ }, "license": "MIT", "agents": [ - "api-scaffolding/agents", - "api-testing-observability/agents", - "backend-api-security/agents", - "backend-development/agents", - "c4-architecture/agents", - "database-cloud-optimization/agents", - "database-design/agents", - "database-migrations/agents", - "full-stack-orchestration/agents" + "./api-scaffolding/agents/api-scaffolding-backend-architect.md", + "./api-scaffolding/agents/api-scaffolding-graphql-architect.md", + "./api-scaffolding/agents/django-pro.md", + "./api-scaffolding/agents/fastapi-pro.md", + "./api-testing-observability/agents/api-documenter.md", + "./backend-api-security/agents/backend-api-security-backend-architect.md", + "./backend-api-security/agents/backend-security-coder.md", + "./backend-development/agents/backend-development-backend-architect.md", + "./backend-development/agents/backend-development-graphql-architect.md", + "./backend-development/agents/backend-development-performance-engineer.md", + "./backend-development/agents/backend-development-security-auditor.md", + "./backend-development/agents/backend-development-test-automator.md", + "./backend-development/agents/event-sourcing-architect.md", + "./backend-development/agents/tdd-orchestrator.md", + "./backend-development/agents/temporal-python-pro.md", + "./c4-architecture/agents/c4-code.md", + "./c4-architecture/agents/c4-component.md", + "./c4-architecture/agents/c4-container.md", + "./c4-architecture/agents/c4-context.md", + "./database-cloud-optimization/agents/cloud-architect.md", + "./database-cloud-optimization/agents/database-cloud-optimization-backend-architect.md", + "./database-cloud-optimization/agents/database-cloud-optimization-database-architect.md", + "./database-cloud-optimization/agents/database-optimizer.md", + "./database-design/agents/database-design-database-architect.md", + "./database-design/agents/sql-pro.md", + "./database-migrations/agents/database-admin.md", + "./database-migrations/agents/database-optimizer.md", + "./full-stack-orchestration/agents/deployment-engineer.md", + "./full-stack-orchestration/agents/full-stack-orchestration-performance-engineer.md", + "./full-stack-orchestration/agents/full-stack-orchestration-security-auditor.md", + "./full-stack-orchestration/agents/full-stack-orchestration-test-automator.md" ], "commands": [ - "api-testing-observability/commands", - "backend-development/commands", - "c4-architecture/commands", - "database-cloud-optimization/commands", - "database-migrations/commands", - "full-stack-orchestration/commands" + "./api-testing-observability/commands", + "./backend-development/commands", + "./c4-architecture/commands", + "./database-cloud-optimization/commands", + "./database-migrations/commands", + "./full-stack-orchestration/commands" ], "skills": [ - "api-scaffolding/skills", - "backend-development/skills", - "database-design/skills" + "./api-scaffolding/skills", + "./backend-development/skills", + "./database-design/skills" ] } diff --git a/plugins/data-ml/plugin.json b/plugins/data-ml/plugin.json index 5c9a2cf0..1009e96c 100644 --- a/plugins/data-ml/plugin.json +++ b/plugins/data-ml/plugin.json @@ -8,23 +8,29 @@ }, "license": "MIT", "agents": [ - "blockchain-web3/agents", - "data-engineering/agents", - "data-validation-suite/agents", - "llm-application-dev/agents", - "machine-learning-ops/agents", - "quantitative-trading/agents" + "./blockchain-web3/agents/blockchain-developer.md", + "./data-engineering/agents/backend-architect.md", + "./data-engineering/agents/data-engineer.md", + "./data-validation-suite/agents/backend-security-coder.md", + "./llm-application-dev/agents/ai-engineer.md", + "./llm-application-dev/agents/prompt-engineer.md", + "./llm-application-dev/agents/vector-database-engineer.md", + "./machine-learning-ops/agents/data-scientist.md", + "./machine-learning-ops/agents/ml-engineer.md", + "./machine-learning-ops/agents/mlops-engineer.md", + "./quantitative-trading/agents/quant-analyst.md", + "./quantitative-trading/agents/risk-manager.md" ], "commands": [ - "data-engineering/commands", - "llm-application-dev/commands", - "machine-learning-ops/commands" + "./data-engineering/commands", + "./llm-application-dev/commands", + "./machine-learning-ops/commands" ], "skills": [ - "blockchain-web3/skills", - "data-engineering/skills", - "llm-application-dev/skills", - "machine-learning-ops/skills", - "quantitative-trading/skills" + "./blockchain-web3/skills", + "./data-engineering/skills", + "./llm-application-dev/skills", + "./machine-learning-ops/skills", + "./quantitative-trading/skills" ] } diff --git a/plugins/devops-cloud/plugin.json b/plugins/devops-cloud/plugin.json index 5e005f11..b5abfd09 100644 --- a/plugins/devops-cloud/plugin.json +++ b/plugins/devops-cloud/plugin.json @@ -8,27 +8,48 @@ }, "license": "MIT", "agents": [ - "application-performance/agents", - "cicd-automation/agents", - "cloud-infrastructure/agents", - "deployment-strategies/agents", - "deployment-validation/agents", - "incident-response/agents", - "kubernetes-operations/agents", - "observability-monitoring/agents" + "./application-performance/agents/frontend-developer.md", + "./application-performance/agents/observability-engineer.md", + "./application-performance/agents/performance-engineer.md", + "./cicd-automation/agents/cicd-automation-cloud-architect.md", + "./cicd-automation/agents/cicd-automation-deployment-engineer.md", + "./cicd-automation/agents/cicd-automation-devops-troubleshooter.md", + "./cicd-automation/agents/kubernetes-architect.md", + "./cicd-automation/agents/terraform-specialist.md", + "./cloud-infrastructure/agents/cloud-infrastructure-cloud-architect.md", + "./cloud-infrastructure/agents/cloud-infrastructure-deployment-engineer.md", + "./cloud-infrastructure/agents/hybrid-cloud-architect.md", + "./cloud-infrastructure/agents/kubernetes-architect.md", + "./cloud-infrastructure/agents/network-engineer.md", + "./cloud-infrastructure/agents/service-mesh-expert.md", + "./cloud-infrastructure/agents/terraform-specialist.md", + "./deployment-strategies/agents/deployment-strategies-deployment-engineer.md", + "./deployment-strategies/agents/terraform-specialist.md", + "./deployment-validation/agents/deployment-validation-cloud-architect.md", + "./incident-response/agents/code-reviewer.md", + "./incident-response/agents/debugger.md", + "./incident-response/agents/error-detective.md", + "./incident-response/agents/incident-responder.md", + "./incident-response/agents/incident-response-devops-troubleshooter.md", + "./incident-response/agents/test-automator.md", + "./kubernetes-operations/agents/kubernetes-architect.md", + "./observability-monitoring/agents/database-optimizer.md", + "./observability-monitoring/agents/network-engineer.md", + "./observability-monitoring/agents/observability-engineer.md", + "./observability-monitoring/agents/performance-engineer.md" ], "commands": [ - "application-performance/commands", - "cicd-automation/commands", - "deployment-validation/commands", - "incident-response/commands", - "observability-monitoring/commands" + "./application-performance/commands", + "./cicd-automation/commands", + "./deployment-validation/commands", + "./incident-response/commands", + "./observability-monitoring/commands" ], "skills": [ - "cicd-automation/skills", - "cloud-infrastructure/skills", - "incident-response/skills", - "kubernetes-operations/skills", - "observability-monitoring/skills" + "./cicd-automation/skills", + "./cloud-infrastructure/skills", + "./incident-response/skills", + "./kubernetes-operations/skills", + "./observability-monitoring/skills" ] } diff --git a/plugins/docs-content/plugin.json b/plugins/docs-content/plugin.json index bdd4ec9f..7fb0e28c 100644 --- a/plugins/docs-content/plugin.json +++ b/plugins/docs-content/plugin.json @@ -8,18 +8,32 @@ }, "license": "MIT", "agents": [ - "code-documentation/agents", - "content-marketing/agents", - "documentation-generation/agents", - "seo-analysis-monitoring/agents", - "seo-content-creation/agents", - "seo-technical-optimization/agents" + "./code-documentation/agents/code-reviewer.md", + "./code-documentation/agents/docs-architect.md", + "./code-documentation/agents/tutorial-engineer.md", + "./content-marketing/agents/content-marketer.md", + "./content-marketing/agents/search-specialist.md", + "./documentation-generation/agents/api-documenter.md", + "./documentation-generation/agents/docs-architect.md", + "./documentation-generation/agents/mermaid-expert.md", + "./documentation-generation/agents/reference-builder.md", + "./documentation-generation/agents/tutorial-engineer.md", + "./seo-analysis-monitoring/agents/seo-authority-builder.md", + "./seo-analysis-monitoring/agents/seo-cannibalization-detector.md", + "./seo-analysis-monitoring/agents/seo-content-refresher.md", + "./seo-content-creation/agents/seo-content-auditor.md", + "./seo-content-creation/agents/seo-content-planner.md", + "./seo-content-creation/agents/seo-content-writer.md", + "./seo-technical-optimization/agents/seo-keyword-strategist.md", + "./seo-technical-optimization/agents/seo-meta-optimizer.md", + "./seo-technical-optimization/agents/seo-snippet-hunter.md", + "./seo-technical-optimization/agents/seo-structure-architect.md" ], "commands": [ - "code-documentation/commands", - "documentation-generation/commands" + "./code-documentation/commands", + "./documentation-generation/commands" ], "skills": [ - "documentation-generation/skills" + "./documentation-generation/skills" ] } diff --git a/plugins/frontend-mobile/plugin.json b/plugins/frontend-mobile/plugin.json index a862fcdc..6b4b8e9c 100644 --- a/plugins/frontend-mobile/plugin.json +++ b/plugins/frontend-mobile/plugin.json @@ -8,21 +8,32 @@ }, "license": "MIT", "agents": [ - "frontend-mobile-development/agents", - "frontend-mobile-security/agents", - "game-development/agents", - "multi-platform-apps/agents", - "ui-design/agents" + "./frontend-mobile-development/agents/frontend-developer.md", + "./frontend-mobile-development/agents/mobile-developer.md", + "./frontend-mobile-security/agents/frontend-developer.md", + "./frontend-mobile-security/agents/frontend-security-coder.md", + "./frontend-mobile-security/agents/mobile-security-coder.md", + "./game-development/agents/minecraft-bukkit-pro.md", + "./game-development/agents/unity-developer.md", + "./multi-platform-apps/agents/backend-architect.md", + "./multi-platform-apps/agents/flutter-expert.md", + "./multi-platform-apps/agents/frontend-developer.md", + "./multi-platform-apps/agents/ios-developer.md", + "./multi-platform-apps/agents/mobile-developer.md", + "./multi-platform-apps/agents/ui-ux-designer.md", + "./ui-design/agents/accessibility-expert.md", + "./ui-design/agents/design-system-architect.md", + "./ui-design/agents/ui-designer.md" ], "commands": [ - "frontend-mobile-development/commands", - "frontend-mobile-security/commands", - "multi-platform-apps/commands", - "ui-design/commands" + "./frontend-mobile-development/commands", + "./frontend-mobile-security/commands", + "./multi-platform-apps/commands", + "./ui-design/commands" ], "skills": [ - "frontend-mobile-development/skills", - "game-development/skills", - "ui-design/skills" + "./frontend-mobile-development/skills", + "./game-development/skills", + "./ui-design/skills" ] } diff --git a/plugins/languages/plugin.json b/plugins/languages/plugin.json index bc4349b5..80cc2feb 100644 --- a/plugins/languages/plugin.json +++ b/plugins/languages/plugin.json @@ -37,27 +37,38 @@ ], "license": "MIT", "agents": [ - "arm-cortex-microcontrollers/agents", - "dotnet-contribution/agents", - "functional-programming/agents", - "javascript-typescript/agents", - "julia-development/agents", - "jvm-languages/agents", - "python-development/agents", - "shell-scripting/agents", - "systems-programming/agents", - "web-scripting/agents" + "./arm-cortex-microcontrollers/agents/arm-cortex-expert.md", + "./dotnet-contribution/agents/dotnet-architect.md", + "./functional-programming/agents/elixir-pro.md", + "./functional-programming/agents/haskell-pro.md", + "./javascript-typescript/agents/javascript-pro.md", + "./javascript-typescript/agents/typescript-pro.md", + "./julia-development/agents/julia-pro.md", + "./jvm-languages/agents/csharp-pro.md", + "./jvm-languages/agents/java-pro.md", + "./jvm-languages/agents/scala-pro.md", + "./python-development/agents/django-pro.md", + "./python-development/agents/fastapi-pro.md", + "./python-development/agents/python-pro.md", + "./shell-scripting/agents/bash-pro.md", + "./shell-scripting/agents/posix-shell-pro.md", + "./systems-programming/agents/c-pro.md", + "./systems-programming/agents/cpp-pro.md", + "./systems-programming/agents/golang-pro.md", + "./systems-programming/agents/rust-pro.md", + "./web-scripting/agents/php-pro.md", + "./web-scripting/agents/ruby-pro.md" ], "commands": [ - "javascript-typescript/commands", - "python-development/commands", - "systems-programming/commands" + "./javascript-typescript/commands", + "./python-development/commands", + "./systems-programming/commands" ], "skills": [ - "dotnet-contribution/skills", - "javascript-typescript/skills", - "python-development/skills", - "shell-scripting/skills", - "systems-programming/skills" + "./dotnet-contribution/skills", + "./javascript-typescript/skills", + "./python-development/skills", + "./shell-scripting/skills", + "./systems-programming/skills" ] } diff --git a/plugins/product-team/plugin.json b/plugins/product-team/plugin.json index 0b938f57..39354bb8 100644 --- a/plugins/product-team/plugin.json +++ b/plugins/product-team/plugin.json @@ -8,29 +8,33 @@ }, "license": "Apache-2.0 AND MIT", "agents": [ - "agent-orchestration/agents", - "agent-teams/agents", - "business-analytics/agents", - "conductor/agents", - "context-management/agents", - "customer-sales-automation/agents", - "developer-essentials/agents", - "startup-business-analyst/agents", - "team-collaboration/agents" + "./agent-orchestration/agents/context-manager.md", + "./agent-teams/agents/team-debugger.md", + "./agent-teams/agents/team-implementer.md", + "./agent-teams/agents/team-lead.md", + "./agent-teams/agents/team-reviewer.md", + "./business-analytics/agents/business-analyst.md", + "./conductor/agents/conductor-validator.md", + "./context-management/agents/context-manager.md", + "./customer-sales-automation/agents/customer-support.md", + "./customer-sales-automation/agents/sales-automator.md", + "./developer-essentials/agents/monorepo-architect.md", + "./startup-business-analyst/agents/startup-analyst.md", + "./team-collaboration/agents/dx-optimizer.md" ], "commands": [ - "agent-orchestration/commands", - "agent-teams/commands", - "conductor/commands", - "context-management/commands", - "startup-business-analyst/commands", - "team-collaboration/commands" + "./agent-orchestration/commands", + "./agent-teams/commands", + "./conductor/commands", + "./context-management/commands", + "./startup-business-analyst/commands", + "./team-collaboration/commands" ], "skills": [ - "agent-teams/skills", - "business-analytics/skills", - "conductor/skills", - "developer-essentials/skills", - "startup-business-analyst/skills" + "./agent-teams/skills", + "./business-analytics/skills", + "./conductor/skills", + "./developer-essentials/skills", + "./startup-business-analyst/skills" ] } diff --git a/plugins/quality-testing/plugin.json b/plugins/quality-testing/plugin.json index 83aed52f..40492a16 100644 --- a/plugins/quality-testing/plugin.json +++ b/plugins/quality-testing/plugin.json @@ -8,34 +8,45 @@ }, "license": "MIT", "agents": [ - "accessibility-compliance/agents", - "code-refactoring/agents", - "codebase-cleanup/agents", - "comprehensive-review/agents", - "debugging-toolkit/agents", - "dependency-management/agents", - "distributed-debugging/agents", - "error-debugging/agents", - "error-diagnostics/agents", - "performance-testing-review/agents", - "tdd-workflows/agents", - "unit-testing/agents" + "./accessibility-compliance/agents/ui-visual-validator.md", + "./code-refactoring/agents/code-reviewer.md", + "./code-refactoring/agents/legacy-modernizer.md", + "./codebase-cleanup/agents/code-reviewer.md", + "./codebase-cleanup/agents/test-automator.md", + "./comprehensive-review/agents/architect-review.md", + "./comprehensive-review/agents/code-reviewer.md", + "./comprehensive-review/agents/security-auditor.md", + "./debugging-toolkit/agents/debugger.md", + "./debugging-toolkit/agents/dx-optimizer.md", + "./dependency-management/agents/legacy-modernizer.md", + "./distributed-debugging/agents/devops-troubleshooter.md", + "./distributed-debugging/agents/error-detective.md", + "./error-debugging/agents/debugger.md", + "./error-debugging/agents/error-detective.md", + "./error-diagnostics/agents/debugger.md", + "./error-diagnostics/agents/error-detective.md", + "./performance-testing-review/agents/performance-engineer.md", + "./performance-testing-review/agents/test-automator.md", + "./tdd-workflows/agents/code-reviewer.md", + "./tdd-workflows/agents/tdd-orchestrator.md", + "./unit-testing/agents/debugger.md", + "./unit-testing/agents/test-automator.md" ], "commands": [ - "accessibility-compliance/commands", - "code-refactoring/commands", - "codebase-cleanup/commands", - "comprehensive-review/commands", - "debugging-toolkit/commands", - "dependency-management/commands", - "distributed-debugging/commands", - "error-debugging/commands", - "error-diagnostics/commands", - "performance-testing-review/commands", - "tdd-workflows/commands", - "unit-testing/commands" + "./accessibility-compliance/commands", + "./code-refactoring/commands", + "./codebase-cleanup/commands", + "./comprehensive-review/commands", + "./debugging-toolkit/commands", + "./dependency-management/commands", + "./distributed-debugging/commands", + "./error-debugging/commands", + "./error-diagnostics/commands", + "./performance-testing-review/commands", + "./tdd-workflows/commands", + "./unit-testing/commands" ], "skills": [ - "accessibility-compliance/skills" + "./accessibility-compliance/skills" ] } diff --git a/plugins/security/plugin.json b/plugins/security/plugin.json index 425d2a59..c3fe85ce 100644 --- a/plugins/security/plugin.json +++ b/plugins/security/plugin.json @@ -8,16 +8,18 @@ }, "license": "MIT", "agents": [ - "hr-legal-compliance/agents", - "security-compliance/agents", - "security-scanning/agents" + "./hr-legal-compliance/agents/hr-pro.md", + "./hr-legal-compliance/agents/legal-advisor.md", + "./security-compliance/agents/security-auditor.md", + "./security-scanning/agents/security-auditor.md", + "./security-scanning/agents/threat-modeling-expert.md" ], "commands": [ - "security-compliance/commands", - "security-scanning/commands" + "./security-compliance/commands", + "./security-scanning/commands" ], "skills": [ - "hr-legal-compliance/skills", - "security-scanning/skills" + "./hr-legal-compliance/skills", + "./security-scanning/skills" ] } diff --git a/plugins/specialized/plugin.json b/plugins/specialized/plugin.json index 814c74dc..3c41f7f6 100644 --- a/plugins/specialized/plugin.json +++ b/plugins/specialized/plugin.json @@ -25,18 +25,21 @@ ], "license": "MIT", "agents": [ - "framework-migration/agents", - "git-pr-workflows/agents", - "payment-processing/agents", - "reverse-engineering/agents" + "./framework-migration/agents/architect-review.md", + "./framework-migration/agents/legacy-modernizer.md", + "./git-pr-workflows/agents/code-reviewer.md", + "./payment-processing/agents/payment-integration.md", + "./reverse-engineering/agents/firmware-analyst.md", + "./reverse-engineering/agents/malware-analyst.md", + "./reverse-engineering/agents/reverse-engineer.md" ], "commands": [ - "framework-migration/commands", - "git-pr-workflows/commands" + "./framework-migration/commands", + "./git-pr-workflows/commands" ], "skills": [ - "framework-migration/skills", - "payment-processing/skills", - "reverse-engineering/skills" + "./framework-migration/skills", + "./payment-processing/skills", + "./reverse-engineering/skills" ] } diff --git a/scripts/generate-domain-plugins.mjs b/scripts/generate-domain-plugins.mjs index a93a441c..9de154d1 100644 --- a/scripts/generate-domain-plugins.mjs +++ b/scripts/generate-domain-plugins.mjs @@ -22,7 +22,7 @@ // referenced by .claude-plugin/marketplace.json. Delete them, then // regenerate the marketplace: node scripts/generate-marketplace.mjs -import { readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { readdirSync, readFileSync, writeFileSync, existsSync, statSync } from 'node:fs'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -75,12 +75,31 @@ function buildDomainManifest(domain) { // Component paths: array of each sub-plugin's own subdirectory, only // when that sub-plugin actually has one (mirrors the pre-consolidation - // per-plugin shape — nothing is invented or flattened). - for (const kind of ['agents', 'commands', 'skills']) { + // per-plugin shape — nothing is invented or flattened). Claude Code's + // plugin manifest schema requires every path field to be relative to the + // plugin root AND start with "./" — a bare "sub/agents" fails manifest + // validation ("Invalid input") even though it looks like a valid relative + // path. See docs/en/plugins-reference#path-behavior-rules. + // + // "commands" and "skills" accept directory paths (skills are directories + // by definition; commands docs explicitly allow "files or directories"). + // "agents" does not — its documented schema and every example show only + // individual .md file paths, never a bare directory — so each sub-plugin's + // agents/ folder must be expanded to its individual file entries. + for (const kind of ['commands', 'skills']) { const paths = subDirs.filter((sub) => existsSync(join(PLUGINS_DIR, domain, sub, kind))) - .map((sub) => `${sub}/${kind}`); + .map((sub) => `./${sub}/${kind}`); if (paths.length) entry[kind] = paths; } + const agentPaths = subDirs.flatMap((sub) => { + const dir = join(PLUGINS_DIR, domain, sub, 'agents'); + if (!existsSync(dir)) return []; + return readdirSync(dir) + .filter((f) => f.endsWith('.md') && statSync(join(dir, f)).isFile()) + .sort() + .map((f) => `./${sub}/agents/${f}`); + }); + if (agentPaths.length) entry.agents = agentPaths; return entry; } From 5b1aa49267204405388d9e60977406f1ad3f2453 Mon Sep 17 00:00:00 2001 From: Richardson Gunde Date: Sun, 26 Jul 2026 16:31:18 +0530 Subject: [PATCH 2/3] Fix backend-inventory ENOTDIR regression from agents-as-files manifest change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buildBackendInventory's collectAgentItems assumed every plugin.json "agents" entry was a directory and called readdir() on it directly. That broke once domain manifests started listing individual .md agent files (per this PR's earlier commit), tripping ENOTDIR in CI (backend-inventory.test.js, caught by the Node 18/20/22 test jobs). resolveMarkdownFiles() now stats each entry and handles either shape (directory -> recursive walk, single .md file -> itself) — matching Claude Code's own plugin manifest schema, which documents `agents` as accepting both directories and individual files. --- src/core/inventory/backend-inventory.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/inventory/backend-inventory.js b/src/core/inventory/backend-inventory.js index 76d882d4..f69e24f6 100644 --- a/src/core/inventory/backend-inventory.js +++ b/src/core/inventory/backend-inventory.js @@ -144,8 +144,20 @@ function makeItem({ }; } +// A manifest `agents` entry may resolve to either a directory (walked +// recursively for .md files) or an individual .md file (Claude Code's plugin +// schema accepts both shapes for this field) — resolve either into a flat +// file list instead of assuming scandir() always works. +async function resolveMarkdownFiles(entryPath) { + if (!(await exists(entryPath))) return []; + const info = await stat(entryPath); + if (info.isDirectory()) return listFilesRecursive(entryPath, (file) => file.endsWith('.md')); + if (info.isFile() && entryPath.endsWith('.md')) return [entryPath]; + return []; +} + async function collectAgentItems(items, { root, dir, runtimeAvailability, fallbackDomain }) { - const files = await listFilesRecursive(dir, (file) => file.endsWith('.md')); + const files = await resolveMarkdownFiles(dir); for (const file of files) { const meta = await readMarkdownMeta(file); const sourcePath = relativeSource(root, file); From f9e7b3253f0fcb00000fcce5300a76db0a678d9d Mon Sep 17 00:00:00 2001 From: Richardson Gunde Date: Sun, 26 Jul 2026 16:40:10 +0530 Subject: [PATCH 3/3] Add test coverage for domain-manifest path generation (Qodo review, PR #469) Qodo flagged that the ./-prefix + agents-as-files generator change shipped with no test coverage. Export buildDomainManifest with an injectable pluginsDir (same pattern as generate-marketplace.mjs's buildMarketplace(root)) so it's testable against a fixture tree instead of only the real plugins/ dir (whose per-sub-plugin manifests are deleted after a real generation run). Adds: - fixture-based unit tests for the ./-prefix and agents-as-files rules - a static assertion that all 10 committed domain manifests conform CLI behavior (buildDomainManifest(domain) defaulting to the real plugins/ dir) is unchanged. --- scripts/generate-domain-plugins.mjs | 18 ++-- tests/generate-domain-plugins.test.js | 119 ++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 tests/generate-domain-plugins.test.js diff --git a/scripts/generate-domain-plugins.mjs b/scripts/generate-domain-plugins.mjs index 9de154d1..881ede03 100644 --- a/scripts/generate-domain-plugins.mjs +++ b/scripts/generate-domain-plugins.mjs @@ -34,17 +34,21 @@ const DOMAINS = [ 'data-ml', 'quality-testing', 'docs-content', 'product-team', 'specialized', ]; -function readSubPluginManifest(domain, sub) { - return JSON.parse(readFileSync(join(PLUGINS_DIR, domain, sub, 'plugin.json'), 'utf8')); +function readSubPluginManifest(pluginsDir, domain, sub) { + return JSON.parse(readFileSync(join(pluginsDir, domain, sub, 'plugin.json'), 'utf8')); } -function buildDomainManifest(domain) { - const subDirs = readdirSync(join(PLUGINS_DIR, domain), { withFileTypes: true }) +// pluginsDir is injectable (mirrors generate-marketplace.mjs's buildMarketplace(root)) +// so tests can point it at a fixture tree instead of the real plugins/ dir — the real +// per-sub-plugin plugin.json files this reads from are deleted after a real generation +// run (see the file-header note), so the real tree alone isn't re-runnable for tests. +export function buildDomainManifest(domain, pluginsDir = PLUGINS_DIR) { + const subDirs = readdirSync(join(pluginsDir, domain), { withFileTypes: true }) .filter((e) => e.isDirectory()) .map((e) => e.name) .sort(); - const subManifests = subDirs.map((sub) => ({ sub, manifest: readSubPluginManifest(domain, sub) })); + const subManifests = subDirs.map((sub) => ({ sub, manifest: readSubPluginManifest(pluginsDir, domain, sub) })); // Attribution: single author if every sub-plugin shares one; otherwise a // `contributors` array naming each author and which sub-plugins are theirs. @@ -87,12 +91,12 @@ function buildDomainManifest(domain) { // individual .md file paths, never a bare directory — so each sub-plugin's // agents/ folder must be expanded to its individual file entries. for (const kind of ['commands', 'skills']) { - const paths = subDirs.filter((sub) => existsSync(join(PLUGINS_DIR, domain, sub, kind))) + const paths = subDirs.filter((sub) => existsSync(join(pluginsDir, domain, sub, kind))) .map((sub) => `./${sub}/${kind}`); if (paths.length) entry[kind] = paths; } const agentPaths = subDirs.flatMap((sub) => { - const dir = join(PLUGINS_DIR, domain, sub, 'agents'); + const dir = join(pluginsDir, domain, sub, 'agents'); if (!existsSync(dir)) return []; return readdirSync(dir) .filter((f) => f.endsWith('.md') && statSync(join(dir, f)).isFile()) diff --git a/tests/generate-domain-plugins.test.js b/tests/generate-domain-plugins.test.js new file mode 100644 index 00000000..b5edbb92 --- /dev/null +++ b/tests/generate-domain-plugins.test.js @@ -0,0 +1,119 @@ +/** + * Domain plugin manifest generation (#468 follow-up, Qodo-flagged): pins the + * Claude Code manifest path rules a real /plugin install validated against — + * every commands/skills/agents path must start with "./", and agents must be + * individual .md file entries (Claude Code's schema doesn't accept a bare + * agents directory, unlike commands/skills). A regression here reintroduces + * the exact "Invalid input" install failure #468 fixed. + * + * owner: RStack developed by Richardson Gunde + */ + +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readdirSync, readFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { buildDomainManifest } from '../scripts/generate-domain-plugins.mjs'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +function makeSubPlugin(pluginsDir, domain, sub, { author, license, agents = [], commands = [], skills = [] }) { + const dir = join(pluginsDir, domain, sub); + mkdirSync(dir, { recursive: true }); + writeFileSync(join(dir, 'plugin.json'), JSON.stringify({ + name: sub, + description: `${sub} description`, + author, + license, + })); + if (agents.length) { + const agentsDir = join(dir, 'agents'); + mkdirSync(agentsDir, { recursive: true }); + for (const name of agents) writeFileSync(join(agentsDir, name), '---\nname: x\n---\nbody'); + } + if (commands.length) { + const commandsDir = join(dir, 'commands'); + mkdirSync(commandsDir, { recursive: true }); + for (const name of commands) writeFileSync(join(commandsDir, name), '---\ndescription: x\n---\nbody'); + } + if (skills.length) { + for (const skill of skills) { + const skillDir = join(dir, 'skills', skill); + mkdirSync(skillDir, { recursive: true }); + writeFileSync(join(skillDir, 'SKILL.md'), '---\nname: x\n---\nbody'); + } + } +} + +test('buildDomainManifest: agents/commands/skills paths satisfy Claude Code path rules', () => { + const pluginsDir = mkdtempSync(join(tmpdir(), 'rstack-domain-plugins-')); + try { + makeSubPlugin(pluginsDir, 'testdomain', 'sub-a', { + author: { name: 'Author One' }, + license: 'MIT', + agents: ['zeta.md', 'alpha.md'], + commands: ['cmd.md'], + skills: ['skill-one'], + }); + + const manifest = buildDomainManifest('testdomain', pluginsDir); + + // Every path field entry must start with "./" (Claude Code plugin manifest + // spec: "all paths must be relative to the plugin root and start with ./"). + for (const kind of ['agents', 'commands', 'skills']) { + assert.ok(Array.isArray(manifest[kind]), `${kind} is an array`); + for (const entry of manifest[kind]) { + assert.ok(entry.startsWith('./'), `${kind} entry "${entry}" must start with ./`); + } + } + + // agents must be individual .md files, not a bare directory reference — + // Claude Code's documented agents schema only shows file paths. + assert.deepEqual(manifest.agents, ['./sub-a/agents/alpha.md', './sub-a/agents/zeta.md']); + + // commands/skills may stay directory-level (both explicitly support it). + assert.deepEqual(manifest.commands, ['./sub-a/commands']); + assert.deepEqual(manifest.skills, ['./sub-a/skills']); + } finally { + rmSync(pluginsDir, { recursive: true, force: true }); + } +}); + +test('buildDomainManifest: sub-plugin with no agents/commands/skills folders omits those fields', () => { + const pluginsDir = mkdtempSync(join(tmpdir(), 'rstack-domain-plugins-')); + try { + makeSubPlugin(pluginsDir, 'testdomain', 'sub-b', { author: { name: 'A' }, license: 'MIT' }); + const manifest = buildDomainManifest('testdomain', pluginsDir); + assert.equal(manifest.agents, undefined); + assert.equal(manifest.commands, undefined); + assert.equal(manifest.skills, undefined); + } finally { + rmSync(pluginsDir, { recursive: true, force: true }); + } +}); + +test('the 10 committed domain plugin manifests all satisfy the Claude Code path rules', () => { + const pluginsDir = join(__dirname, '..', 'plugins'); + const domains = readdirSync(pluginsDir, { withFileTypes: true }) + .filter((e) => e.isDirectory() && e.name !== 'sdlc-rstack') + .map((e) => e.name); + assert.ok(domains.length >= 10, 'expected at least the 10 consolidated domain plugin folders'); + + for (const domain of domains) { + const manifestPath = join(pluginsDir, domain, 'plugin.json'); + const parsed = JSON.parse(readFileSync(manifestPath, 'utf8')); + for (const kind of ['agents', 'commands', 'skills']) { + if (!Array.isArray(parsed[kind])) continue; + for (const entry of parsed[kind]) { + assert.ok(entry.startsWith('./'), `${domain}: ${kind} entry "${entry}" must start with ./`); + } + } + if (Array.isArray(parsed.agents)) { + for (const entry of parsed.agents) { + assert.ok(entry.endsWith('.md'), `${domain}: agents entry "${entry}" must be an individual .md file`); + } + } + } +});