refactor(package): publish skvlt cli

This commit is contained in:
xixu-me committed 2026-03-25 12:12:25 +08:00
commit 32545773c9
22 files changed
+2548

No files matched your search

+123
View File
@@ -0,0 +1,123 @@
import { expect, test } from "bun:test";
import {
parseManifestText,
type Manifest,
} from "../src/internal/manifest/parse-manifest";
import { runRestore } from "../src/commands/restore";
const sampleManifest: Manifest = {
totalSources: 2,
totalSkills: 3,
sources: new Map([
["anthropics/skills", { count: 2, skills: ["alpha", "beta"] }],
["github/awesome-copilot", { count: 1, skills: ["gamma"] }],
]),
};
test("shows restore help text", async () => {
const result = await runRestore(["--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("Usage:");
expect(result.stdout).toContain("skvlt restore");
});
test("prints per-source install commands during restore dry-run", async () => {
const result = await runRestore(
["--dry-run", "--only-source", "anthropics/skills", "--concurrency", "4"],
{
readManifest: () => sampleManifest,
},
);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain(
"Parallel install concurrency: 1 (configured).",
);
expect(result.stdout).toContain(
"[dry-run] bunx skills add anthropics/skills -g -y --skill alpha beta",
);
expect(result.stdout).not.toContain("github/awesome-copilot");
});
test("restores only missing skills during live execution", async () => {
const commands: string[][] = [];
const result = await runRestore(["--manifest", "ignored.yaml"], {
readManifest: () => sampleManifest,
getInstalledSkillNames: async () => new Set(["alpha"]),
runBunx: async (args) => {
commands.push(args);
return { exitCode: 0, stdout: "", stderr: "" };
},
});
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("2 pending, 1 already installed.");
expect(result.stdout).toContain(
"Installing 1 missing skill(s) from anthropics/skills and skipping 1 already installed.",
);
expect(commands).toEqual([
["skills", "add", "anthropics/skills", "-g", "-y", "--skill", "beta"],
[
"skills",
"add",
"github/awesome-copilot",
"-g",
"-y",
"--skill",
"gamma",
],
]);
});
test("continues and returns success when continue-on-error is enabled", async () => {
const result = await runRestore(["--manifest", "ignored.yaml", "--continue-on-error"], {
readManifest: () => sampleManifest,
getInstalledSkillNames: async () => new Set(),
runBunx: async (args) => {
if (args[2] === "anthropics/skills") {
return { exitCode: 1, stdout: "", stderr: "source failed" };
}
return { exitCode: 0, stdout: "installed gamma", stderr: "" };
},
});
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain(
"Installing from 2 source(s) with concurrency 2 (auto).",
);
expect(result.stderr).toContain("source failed");
expect(result.stderr).toContain(
"Install failed for source 'anthropics/skills' with exit code 1",
);
expect(result.stderr).toContain(
"Completed restore with 1 source failure(s).",
);
});
test("parses manifest text with quoted source and skill names", () => {
const manifest = parseManifestText(
[
"total_sources: 1",
"total_skills: 2",
"",
"sources:",
" 'owner/repo':",
" count: 2",
" skills:",
" - 'simple'",
' - "needs \\"quotes\\""',
"",
].join("\n"),
"memory.yaml",
);
expect(manifest.totalSources).toBe(1);
expect(manifest.totalSkills).toBe(2);
expect(manifest.sources.get("owner/repo")).toEqual({
count: 2,
skills: ["simple", 'needs "quotes"'],
});
});