diff --git a/apps/server/src/services/skills/builtin-skills/submit-a-plugin/SKILL.md b/apps/server/src/services/skills/builtin-skills/submit-a-plugin/SKILL.md index da959b682..92fff0243 100644 --- a/apps/server/src/services/skills/builtin-skills/submit-a-plugin/SKILL.md +++ b/apps/server/src/services/skills/builtin-skills/submit-a-plugin/SKILL.md @@ -215,7 +215,7 @@ Include these required fields: - `author` - `source` -Use `tags` and `engines` when they provide useful search or compatibility data. +Use `tags` when they provide useful search data. The `id` must match the filename and the plugin manifest ID. @@ -227,7 +227,7 @@ Do not use claims such as "best," "powerful," or "easy." Describe observed behav Use up to ten specific lowercase tags. Use hyphens in tags with multiple words. -Copy honest engine ranges from the plugin manifest. A marketplace entry can narrow those ranges but cannot widen them. +Keep engine ranges in the plugin manifest. The marketplace entry does not declare compatibility. Set `author.github` to the GitHub account that opens the pull request. @@ -253,10 +253,6 @@ Use this shape as a guide. Confirm every field against the current schema. "github": "acme", "url": "https://acme.example" }, - "engines": { - "bb": ">=0.40.0", - "bbPluginSdk": ">=0.5.0" - }, "source": { "git": { "url": "https://github.com/acme/bb-plugin-notes.git", diff --git a/apps/server/test/skills/submit-a-plugin-schema.repro.test.ts b/apps/server/test/skills/submit-a-plugin-schema.repro.test.ts new file mode 100644 index 000000000..925d37888 --- /dev/null +++ b/apps/server/test/skills/submit-a-plugin-schema.repro.test.ts @@ -0,0 +1,41 @@ +import { readFile } from "node:fs/promises"; +import { fileURLToPath } from "node:url"; +import { Ajv2020 } from "ajv/dist/2020.js"; +import { describe, expect, it } from "vitest"; +import { z } from "zod"; + +const skillPath = fileURLToPath( + new URL( + "../../src/services/skills/builtin-skills/submit-a-plugin/SKILL.md", + import.meta.url, + ), +); +const schemaPath = fileURLToPath( + new URL("../../../web/public/schemas/marketplace.schema.json", import.meta.url), +); + +describe("submit-a-plugin marketplace entry example", () => { + it("conforms to the published marketplace schema", async () => { + const skill = await readFile(skillPath, "utf8"); + const marker = "Use this shape as a guide. Confirm every field against the current schema."; + const markerIndex = skill.indexOf(marker); + if (markerIndex < 0) throw new Error("entry example marker is missing"); + + const match = /```json\n([\s\S]*?)\n```/u.exec(skill.slice(markerIndex)); + if (match?.[1] === undefined) throw new Error("entry JSON example is missing"); + const entry = JSON.parse(match[1]); + + const schema = z.record(z.string(), z.unknown()).parse( + JSON.parse(await readFile(schemaPath, "utf8")), + ); + const validate = new Ajv2020({ strict: false }).compile(schema); + const manifest = { + schemaVersion: 1, + name: "example", + displayName: "Example plugins", + plugins: [entry], + }; + + expect(validate(manifest), JSON.stringify(validate.errors)).toBe(true); + }); +});