/**
* Tests for plugin-resident skill discovery in `loadSkillCatalog`.
*
* The catalog surfaces skills a plugin ships at `plugins/
/skills//`.
* The gate is a loadable manifest (parseable `package.json` with a non-empty
* `name`) — the manifest `name` need NOT equal the install directory name.
* That match must not be required: a plugin is installed under its marketplace
* slug or GitHub path leaf, which routinely differs from its `package.json`
* `name` (e.g. cognee installs to `cognee`/`vellum-assistant` while its package
* is named `cognee-memory`). Requiring the match silently drops the plugin's
* skills even though the runtime loads its hooks and tools fine.
*/
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { beforeEach, describe, expect, test } from "bun:test";
import { getWorkspacePluginsDir } from "../../util/platform.js";
import { loadSkillCatalog } from "../skills.js";
function writePlugin(
dirName: string,
packageName: string,
skills: Array<{ id: string; description: string }>,
opts: { disabled?: boolean; packageJson?: string | null } = {},
): void {
const pluginDir = join(getWorkspacePluginsDir(), dirName);
mkdirSync(pluginDir, { recursive: true });
if (opts.packageJson === null) {
// Intentionally omit package.json.
} else {
writeFileSync(
join(pluginDir, "package.json"),
opts.packageJson ??
JSON.stringify({
name: packageName,
version: "1.0.0",
peerDependencies: { "@vellumai/plugin-api": "*" },
}),
);
}
if (opts.disabled) {
writeFileSync(join(pluginDir, ".disabled"), "");
}
for (const skill of skills) {
const skillDir = join(pluginDir, "skills", skill.id);
mkdirSync(skillDir, { recursive: true });
writeFileSync(
join(skillDir, "SKILL.md"),
`---\nname: ${skill.id}\ndescription: ${skill.description}\n---\n\nBody for ${skill.id}.\n`,
);
}
}
function skillById(id: string) {
return loadSkillCatalog().find((s) => s.id === id);
}
describe("discoverPluginResidentSkills (via loadSkillCatalog)", () => {
beforeEach(() => {
const pluginsDir = getWorkspacePluginsDir();
if (existsSync(pluginsDir)) {
rmSync(pluginsDir, { recursive: true, force: true });
}
});
test("surfaces a plugin skill when the install dir name differs from package.json name", () => {
// dir `vellum-assistant` (GitHub path leaf) vs package `cognee-memory`:
// the exact cognee shape that was silently dropped before.
writePlugin("vellum-assistant", "cognee-memory", [
{
id: "qa-cognee-sync",
description: "Sync session memory into the graph.",
},
]);
const skill = skillById("qa-cognee-sync");
expect(skill).toBeDefined();
expect(skill!.source).toBe("plugin");
// Attribution is the install slug (directory name), which is the identity
// used by `plugins list` and per-conversation plugin scoping.
expect(skill!.owner).toEqual({ kind: "plugin", id: "vellum-assistant" });
});
test("surfaces a plugin skill when dir name equals package.json name", () => {
writePlugin("demo-matched", "demo-matched", [
{ id: "qa-matched-skill", description: "A matched-name plugin skill." },
]);
const skill = skillById("qa-matched-skill");
expect(skill).toBeDefined();
expect(skill!.owner).toEqual({ kind: "plugin", id: "demo-matched" });
});
test("skips a plugin directory that has no package.json", () => {
writePlugin(
"no-manifest",
"unused",
[{ id: "qa-no-manifest-skill", description: "Should not surface." }],
{ packageJson: null },
);
expect(skillById("qa-no-manifest-skill")).toBeUndefined();
});
test("skips a plugin directory whose package.json is unparseable", () => {
writePlugin(
"bad-manifest",
"unused",
[{ id: "qa-bad-manifest-skill", description: "Should not surface." }],
{ packageJson: "{ not valid json" },
);
expect(skillById("qa-bad-manifest-skill")).toBeUndefined();
});
test("skips a plugin directory whose package.json has no name", () => {
writePlugin(
"nameless",
"unused",
[{ id: "qa-nameless-skill", description: "Should not surface." }],
{ packageJson: JSON.stringify({ version: "1.0.0" }) },
);
expect(skillById("qa-nameless-skill")).toBeUndefined();
});
test("hides resident skills of a disabled plugin", () => {
writePlugin(
"disabled-plugin",
"disabled-pkg",
[{ id: "qa-disabled-skill", description: "Hidden while disabled." }],
{ disabled: true },
);
expect(skillById("qa-disabled-skill")).toBeUndefined();
});
});