evOo-local/packages/adapters/src/skill-github.test.ts
cynarAI 4cd9468b6f
Install skills from a pasted GitHub link (#197)
A new skill_install agent tool reads every SKILL.md behind a public github.com
repository, folder or file link and saves each as one of the user's own,
editable skills. Only GitHub's API and raw hosts are contacted; invalid files
and name clashes are skipped and reported.


Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-10 14:38:04 +02:00

142 lines
5.2 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import type { GithubFetch } from "./skill-github.js";
import { fetchGithubSkills, parseGithubSkillUrl } from "./skill-github.js";
import { skillInstallFromTool } from "./skill-tools.js";
const skill = (name: string) =>
`---\nname: ${name}\ndescription: Pretend ${name} recipe\n---\n\n1. Do the thing.\n`;
/** A pretend public repo: two skills under skills/, one at the root, one unrelated file. */
function pretendGithub(files: Record<string, string>, defaultBranch = "main") {
const calls: string[] = [];
const fetchImpl: GithubFetch = vi.fn(async (url: string) => {
calls.push(url);
const { hostname, pathname } = new URL(url);
if (hostname === "api.github.com" && pathname === "/repos/someone/founder-kit") {
return Response.json({ default_branch: defaultBranch });
}
if (
hostname === "api.github.com" &&
pathname.startsWith("/repos/someone/founder-kit/git/trees/")
) {
return Response.json({
tree: [
...Object.keys(files).map((path) => ({ path, type: "blob" })),
{ path: "README.md", type: "blob" },
{ path: "skills", type: "tree" },
],
});
}
if (hostname === "raw.githubusercontent.com") {
const path = pathname.split("/").slice(4).join("/");
const body = files[decodeURIComponent(path)];
return body === undefined ? new Response("", { status: 404 }) : new Response(body);
}
return new Response("", { status: 404 });
});
return { fetchImpl, calls };
}
function makePrisma() {
const store: Array<Record<string, unknown>> = [];
return {
agentSkill: {
findMany: vi.fn(async () => store.map((row) => ({ ...row }))),
create: vi.fn(async ({ data }: { data: Record<string, unknown> }) => {
const row = { id: `skill-${store.length + 1}`, source: "user", ...data };
store.push(row);
return row;
}),
},
store,
};
}
const owner = { spaceId: "ws-1", userId: "user-1" };
describe("GitHub skill links", () => {
it("reads repository, folder, file and raw links and rejects other hosts", () => {
expect(parseGithubSkillUrl("https://github.com/someone/founder-kit")).toEqual({
owner: "someone",
repo: "founder-kit",
path: "",
});
expect(parseGithubSkillUrl("github.com/someone/founder-kit.git/tree/dev/skills/cfo")).toEqual({
owner: "someone",
repo: "founder-kit",
ref: "dev",
path: "skills/cfo",
});
expect(
parseGithubSkillUrl("https://raw.githubusercontent.com/someone/founder-kit/main/SKILL.md"),
).toMatchObject({ ref: "main", path: "SKILL.md" });
expect(parseGithubSkillUrl("https://example.com/someone/founder-kit")).toBeNull();
expect(parseGithubSkillUrl("https://github.com/someone")).toBeNull();
expect(parseGithubSkillUrl("https://github.com.evil.test/someone/kit")).toBeNull();
});
it("installs every SKILL.md of a repo and only fetches from GitHub", async () => {
const { fetchImpl, calls } = pretendGithub({
"SKILL.md": skill("Launch kit"),
"skills/cfo/SKILL.md": skill("CFO"),
"skills/panel/SKILL.md": skill("Buyer panel"),
});
const prisma = makePrisma();
const result = await skillInstallFromTool(
prisma as never,
owner,
{ url: "https://github.com/someone/founder-kit" },
fetchImpl,
);
expect(result).toMatchObject({
ok: true,
repo: "someone/founder-kit",
installed: ["Launch kit", "CFO", "Buyer panel"],
skipped: [],
});
expect(prisma.store.map((row) => row.source)).toEqual(["user", "user", "user"]);
expect(
calls.every((url) => /^https:\/\/(api\.github\.com|raw\.githubusercontent\.com)\//.test(url)),
).toBe(true);
});
it("limits a folder link to that folder and skips invalid or clashing skills", async () => {
const { fetchImpl } = pretendGithub({
"skills/cfo/SKILL.md": skill("CFO"),
"skills/broken/SKILL.md": "no frontmatter",
"skills/zz-copy/SKILL.md": skill("cfo"),
"other/SKILL.md": skill("Elsewhere"),
});
const prisma = makePrisma();
const result = await skillInstallFromTool(
prisma as never,
owner,
{ url: "https://github.com/someone/founder-kit/tree/main/skills" },
fetchImpl,
);
expect(result).toMatchObject({ ok: true, installed: ["CFO"] });
expect((result.skipped as Array<{ path: string }>).map((s) => s.path)).toEqual([
"skills/broken/SKILL.md",
"skills/zz-copy/SKILL.md",
]);
});
it("explains links that hold no skill", async () => {
const { fetchImpl } = pretendGithub({});
await expect(
fetchGithubSkills("https://github.com/someone/founder-kit", fetchImpl),
).rejects.toThrow("No SKILL.md found");
expect(
await skillInstallFromTool(makePrisma() as never, owner, { url: "https://example.com/x/y" }),
).toEqual({ error: "Paste a github.com link to a repository, folder or SKILL.md." });
expect(
await skillInstallFromTool(
makePrisma() as never,
owner,
{ url: "https://github.com/nobody/missing" },
pretendGithub({}).fetchImpl,
),
).toEqual({ error: "Repository not found or not public." });
});
});