mirror of
https://github.com/vercel/vercel-plugin.git
synced 2026-09-14 15:39:47 +08:00
c9b1d8ff93
* feat(marketplace): discover-first commerce routing + provisioning guidance Make the model reliably reach for the Marketplace when an app needs an external service, instead of hardcoding a provider from memory. - knowledge-update (always-injected at session start): add a discover-first directive — load the `marketplace` skill, then discover + provision a real integration BEFORE planning/writing code/asking. - marketplace SKILL.md: broaden the description (commerce/payments/etc. as the catch-all for capabilities without a dedicated skill); restructure into discover -> install -> build with an anti-mock/anti-punt rule; add a Recommendations section keyed on the product-catalog test (commerce vs payments), with no hardcoded providers (discover names them). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(marketplace): remove command that shadowed the skill; tighten routing The `marketplace` skill and `/marketplace` command shared a name, so the command shadowed the skill — Skill(vercel:marketplace) loaded the command's runbook instead, and the skill's routing guidance never reached the model. Remove the command (marketplace is skill-only, like the other capabilities) so the skill loads, and tighten routing: discover-first flow, a preferred- provider table (commerce -> Shopify, payments -> Stripe), and a slimmer knowledge-update that points at the skill. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(release): patch-bump plugin manifests to 0.45.1 Backwards-compatible fix (marketplace skill routing); bump the version in all three shipped manifests (.claude-plugin, .cursor-plugin, .plugin). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
56 lines
2.4 KiB
TypeScript
56 lines
2.4 KiB
TypeScript
import { describe, test, expect } from "bun:test";
|
|
import { readFileSync } from "node:fs";
|
|
import { join, resolve } from "node:path";
|
|
|
|
// Deterministic content-contract guards for the marketplace catch-all skill.
|
|
// These don't test model behavior (that's the agent-eval layer) — they lock in
|
|
// the *routing surface* and anti-mock/anti-hardcode contract so a future edit
|
|
// can't silently regress the discover-first design.
|
|
|
|
const ROOT = resolve(import.meta.dirname, "..");
|
|
const SKILL = readFileSync(join(ROOT, "skills", "marketplace", "SKILL.md"), "utf8");
|
|
|
|
const fmMatch = SKILL.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
const frontmatter = fmMatch ? fmMatch[1] : "";
|
|
const body = fmMatch ? fmMatch[2] : SKILL;
|
|
const description = (frontmatter.match(/description:\s*(.*)/)?.[1] ?? "").toLowerCase();
|
|
|
|
/** Slice a markdown section from `### heading` up to the next `## ` heading. */
|
|
function section(heading: string): string {
|
|
const start = body.indexOf(heading);
|
|
if (start < 0) return "";
|
|
const rest = body.slice(start);
|
|
const end = rest.indexOf("\n## ");
|
|
return end >= 0 ? rest.slice(0, end) : rest;
|
|
}
|
|
|
|
describe("marketplace skill content contract", () => {
|
|
test("description routes the full catch-all capability set", () => {
|
|
for (const kw of ["commerce", "payments", "search", "cms"]) {
|
|
expect(description).toContain(kw);
|
|
}
|
|
expect(/monitoring|observability/.test(description)).toBe(true);
|
|
expect(/email|messaging/.test(description)).toBe(true);
|
|
});
|
|
|
|
test("commerce vs payments is decided by the product-catalog test, not a provider", () => {
|
|
// The distinction lives in the category→use-case map: catalog → commerce, no catalog → payments.
|
|
expect(body.toLowerCase()).toContain("catalog");
|
|
expect(body).toContain("`commerce`");
|
|
expect(body).toContain("`payments`");
|
|
// steered as CATEGORY slugs, never a named provider default
|
|
expect(body).not.toMatch(/default to (shopify|stripe)/i);
|
|
});
|
|
|
|
test("install is mandatory and mocking is explicitly forbidden", () => {
|
|
expect(body).toMatch(/required, not optional/i);
|
|
expect(body).toMatch(/substitute a mock/i);
|
|
expect(body).toContain("vercel integration add");
|
|
});
|
|
|
|
test("no provider is hardcoded as a forced default anywhere in the skill", () => {
|
|
// Provider names may appear as examples/anti-patterns, but never as "default to <provider>".
|
|
expect(body).not.toMatch(/default(s)? to (shopify|stripe|neon|upstash)\b/i);
|
|
});
|
|
});
|