Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/external-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ body:
id: plugin-path
attributes:
label: Plugin path inside the repository
description: Optional if the plugin lives at the repository root.
placeholder: .github/plugins/my-plugin
description: Optional if the plugin lives at the repository root. Otherwise, enter the folder where the plugin structure starts, not the plugin.json file.
placeholder: plugins/my-plugin
validations:
required: false
- type: input
Expand Down
1 change: 0 additions & 1 deletion .github/plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@
"source": {
"source": "github",
"repo": "ChromeDevTools/chrome-devtools-mcp",
"path": ".github/plugin/plugin.json",
"ref": "chrome-devtools-mcp-v1.0.1"
}
},
Expand Down Expand Up @@ -584,7 +583,7 @@
"description": "MCP server and 12 skills for product analysis, specification writing, strategy, narrative, and stakeholder alignment. Run competitive analyses, write zero-question specs, design metrics, and produce executive-ready documents directly from Copilot.",
"version": "2.1.0",
"author": {
"name": "Parth Sangani",

Check failure on line 586 in .github/plugin/marketplace.json

View workflow job for this annotation

GitHub Actions / codespell

Parth ==> Path
"url": "https://github.com/Avyayalaya"
},
"repository": "https://github.com/Avyayalaya/pm-skills-arsenal",
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ For entries committed to `plugins/external.json`, the current marketplace valida
- `repository` as an HTTPS GitHub URL
- `keywords` as lowercase hyphenated tags
- `source.source: "github"` plus `source.repo` in `owner/repo` format
- optional `source.path` values to stay relative to the repository root
- optional `source.path` values of `/` for repository root, or a repository-relative folder where the plugin structure starts (do not point to `plugin.json` directly)

The public-submission policy builds on those rules and also requires `license` plus an immutable `source.ref`.

Expand Down
24 changes: 24 additions & 0 deletions eng/external-plugin-validation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export const EXTERNAL_PLUGIN_POLICIES = Object.freeze({
}),
});

const EXTERNAL_PLUGIN_ROOT_MANIFEST_PATHS = Object.freeze([
"plugin.json",
".github/plugin/plugin.json",
".plugin/plugin.json",
]);

function resolvePolicy(policy) {
if (!policy) {
return EXTERNAL_PLUGIN_POLICIES.marketplace;
Expand Down Expand Up @@ -203,12 +209,20 @@ function validateHomepage(homepage, prefix, errors) {
validateHttpsUrl(homepage, "homepage", prefix, errors);
}

function formatExpectedPluginRootMessage() {
return EXTERNAL_PLUGIN_ROOT_MANIFEST_PATHS.map((manifestPath) => `"${manifestPath}"`).join(", ");
}

function validateRelativePath(pathValue, prefix, errors) {
if (!isNonEmptyString(pathValue)) {
errors.push(`${prefix}: "source.path" must be a non-empty string when provided`);
return;
}

if (pathValue === "/") {
return;
}

const normalized = path.posix.normalize(pathValue);
const segments = pathValue.split("/");

Expand All @@ -219,6 +233,16 @@ function validateRelativePath(pathValue, prefix, errors) {
if (pathValue.includes("\\")) {
errors.push(`${prefix}: "source.path" must use forward slashes`);
}

if (normalized === ".") {
errors.push(`${prefix}: "source.path" must be "/" for the repository root or a plugin root directory relative to the repository root`);
}

if (path.posix.basename(normalized) === "plugin.json") {
errors.push(
`${prefix}: "source.path" must point to the plugin root directory, not the manifest file; relative to "source.path", expected one of ${formatExpectedPluginRootMessage()}`
);
}
}

function validateImmutableRef(ref, prefix, errors) {
Expand Down
25 changes: 3 additions & 22 deletions plugins/external.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
"source": {
"source": "github",
"repo": "ChromeDevTools/chrome-devtools-mcp",
"path": ".github/plugin/plugin.json",
"ref": "chrome-devtools-mcp-v1.0.1"
}
},
Expand Down Expand Up @@ -194,13 +193,7 @@
"url": "https://www.figma.com"
},
"homepage": "https://github.com/figma/mcp-server-guide",
"keywords": [
"figma",
"design",
"mcp",
"ui",
"code-connect"
],
"keywords": ["figma", "design", "mcp", "ui", "code-connect"],
"repository": "https://github.com/figma/mcp-server-guide",
"source": {
"source": "github",
Expand Down Expand Up @@ -272,14 +265,7 @@
"url": "https://www.microsoft.com"
},
"homepage": "https://github.com/microsoft/Build-CLI",
"keywords": [
"microsoft",
"build",
"ignite",
"events",
"sessions",
"learn"
],
"keywords": ["microsoft", "build", "ignite", "events", "sessions", "learn"],
"license": "Apache-2.0",
"repository": "https://github.com/microsoft/Build-CLI",
"source": {
Expand All @@ -296,12 +282,7 @@
"url": "https://www.microsoft.com"
},
"homepage": "https://github.com/dotnet/modernize-dotnet",
"keywords": [
"modernization",
"upgrade",
"migration",
"dotnet"
],
"keywords": ["modernization", "upgrade", "migration", "dotnet"],
"license": "MIT",
"repository": "https://github.com/dotnet/modernize-dotnet",
"source": {
Expand All @@ -315,7 +296,7 @@
"description": "MCP server and 12 skills for product analysis, specification writing, strategy, narrative, and stakeholder alignment. Run competitive analyses, write zero-question specs, design metrics, and produce executive-ready documents directly from Copilot.",
"version": "2.1.0",
"author": {
"name": "Parth Sangani",

Check failure on line 299 in plugins/external.json

View workflow job for this annotation

GitHub Actions / codespell

Parth ==> Path
"url": "https://github.com/Avyayalaya"
},
"repository": "https://github.com/Avyayalaya/pm-skills-arsenal",
Expand Down
2 changes: 1 addition & 1 deletion website/src/scripts/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ async function openPluginModal(
function getExternalPluginUrl(plugin: Plugin): string {
if (plugin.source?.source === "github" && plugin.source.repo) {
const base = `https://github.com/${plugin.source.repo}`;
return plugin.source.path
return plugin.source.path && plugin.source.path !== "/"
? `${base}/tree/main/${plugin.source.path}`
: base;
}
Expand Down
2 changes: 1 addition & 1 deletion website/src/scripts/pages/plugins-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function sortPlugins<T extends RenderablePlugin>(
function getExternalPluginUrl(plugin: RenderablePlugin): string {
if (plugin.source?.source === 'github' && plugin.source.repo) {
const base = `https://github.com/${plugin.source.repo}`;
return plugin.source.path ? `${base}/tree/main/${plugin.source.path}` : base;
return plugin.source.path && plugin.source.path !== '/' ? `${base}/tree/main/${plugin.source.path}` : base;
}

return sanitizeUrl(plugin.repository || plugin.homepage);
Expand Down
Loading