feat: add category and icon fields to plugin manifest schema

This commit is contained in:
LukeGus committed 2026-09-20 21:06:16 -05:00
1 parent 5a76d30637
commit 7f1beddccc
8 files changed
+72

No files matched your search

@@ -7,6 +7,7 @@
"name": "Jane Doe"
},
"license": "MIT",
"category": "Productivity",
"engine": {
"termix": ">=2.9.0",
"api": "1"
@@ -6,6 +6,7 @@
"name": "Jane Doe"
},
"license": "MIT",
"category": "Productivity",
"engine": {
"termix": ">=2.9.0",
"api": "1"
@@ -0,0 +1,23 @@
{
"id": "sample-plugin",
"name": "Sample Plugin",
"version": "1.0.0",
"description": "Has a category that is not in the known category list.",
"author": {
"name": "Jane Doe"
},
"license": "MIT",
"category": "Miscellaneous",
"engine": {
"termix": ">=2.9.0",
"api": "1"
},
"capabilities": {
"backend": true,
"frontend": true,
"electron": false,
"platforms": ["linux"]
},
"permissions": [],
"sidecars": []
}
@@ -7,6 +7,7 @@
"name": "Jane Doe"
},
"license": "MIT",
"category": "Productivity",
"engine": {
"termix": ">=2.9.0",
"api": "1"
@@ -9,6 +9,8 @@
},
"license": "MIT",
"repository": "https://github.com/example/sample-plugin",
"category": "Productivity",
"icon": "Box",
"engine": {
"termix": ">=2.9.0",
"api": "1"
+19
View File
@@ -12,6 +12,7 @@
"author",
"license",
"engine",
"category",
"capabilities",
"permissions",
"sidecars"
@@ -52,6 +53,24 @@
"type": "string",
"format": "uri"
},
"category": {
"type": "string",
"enum": [
"Terminal",
"Files & Transfer",
"Infrastructure",
"Monitoring",
"Networking",
"Access & Security",
"Productivity"
],
"description": "Store category used for browsing/filtering. Must match one of the categories the store UI filters by."
},
"icon": {
"type": "string",
"minLength": 1,
"description": "Either a Lucide icon name (same convention as contributes.tabs[].icon) or a relative path to an icon.svg shipped in the plugin package root. If both a Lucide name and an icon.svg are present, the registry-index build should prefer icon.svg."
},
"engine": {
"type": "object",
"additionalProperties": false,
+19
View File
@@ -27,6 +27,10 @@ function knownPermissions(schema) {
return schema.properties.permissions.items.enum;
}
function knownCategories(schema) {
return schema.properties.category.enum;
}
function validateManifest(manifest, schema) {
const errors = [];
@@ -56,6 +60,21 @@ function validateManifest(manifest, schema) {
}
}
if (typeof manifest.category === "string") {
const categories = knownCategories(schema);
if (!categories.includes(manifest.category)) {
errors.push(
`Field "category" must be one of: ${categories.join(", ")}, got: "${manifest.category}"`,
);
}
} else if ("category" in manifest) {
errors.push('Field "category" must be a string');
}
if ("icon" in manifest && typeof manifest.icon !== "string") {
errors.push('Field "icon" must be a string');
}
if (manifest.engine && typeof manifest.engine === "object") {
if (!manifest.engine.termix) {
errors.push('Field "engine.termix" is required');
+6
View File
@@ -43,4 +43,10 @@ describe("validate-plugin-manifest.cjs", () => {
expect(status).not.toBe(0);
expect(output).toContain('Unknown permission: "credentials.read"');
});
it("rejects a manifest with an unknown category", () => {
const { status, output } = runValidator("invalid-unknown-category.json");
expect(status).not.toBe(0);
expect(output).toMatch(/Field "category" must be one of/);
});
});