vskill 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents.json +1 -1
- package/dist/commands/plugin.d.ts +9 -0
- package/dist/commands/plugin.js +123 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/core/plugin-validator.d.ts +17 -0
- package/dist/core/plugin-validator.js +45 -0
- package/dist/core/plugin-validator.js.map +1 -0
- package/dist/eval-server/api-routes.js +18 -7
- package/dist/eval-server/api-routes.js.map +1 -1
- package/dist/eval-server/authoring-routes.d.ts +4 -0
- package/dist/eval-server/authoring-routes.js +117 -7
- package/dist/eval-server/authoring-routes.js.map +1 -1
- package/dist/eval-ui/assets/{CreateSkillPage-8zJnqwjr.js → CreateSkillPage-Cvqa6K5i.js} +5 -5
- package/dist/eval-ui/assets/{FindSkillsPalette-C_GiEl_q.js → FindSkillsPalette-CWHvEBnP.js} +2 -2
- package/dist/eval-ui/assets/{SearchPaletteCore-D1atnd7R.js → SearchPaletteCore-CqA31vP2.js} +2 -2
- package/dist/eval-ui/assets/{SkillDetailPanel-Cg8-E-fm.js → SkillDetailPanel-u4KHBlUQ.js} +1 -1
- package/dist/eval-ui/assets/{UpdateDropdown-DvdC8JlL.js → UpdateDropdown-DslQOd6_.js} +1 -1
- package/dist/eval-ui/assets/index-DVbvMGAg.js +122 -0
- package/dist/eval-ui/index.html +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/eval-ui/assets/index-Dr23ls2i.js +0 -122
package/agents.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
interface PluginNewOpts {
|
|
3
|
+
description?: string;
|
|
4
|
+
withSkill?: string;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function pluginNewCommand(pluginName: string, opts: PluginNewOpts): Promise<void>;
|
|
8
|
+
export declare function registerPluginCommand(program: Command): void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// 0793 — `vskill plugin <subcommand>` family.
|
|
3
|
+
//
|
|
4
|
+
// Today: `new <name>` scaffolds a Claude Code plugin (`.claude-plugin/plugin.json`)
|
|
5
|
+
// with optional first skill (`--with-skill <slug>`). We delegate schema
|
|
6
|
+
// validation to `claude plugin validate <path>` so vskill never duplicates
|
|
7
|
+
// Claude Code's plugin schema. When `claude` is not on PATH, validation
|
|
8
|
+
// soft-skips with a warning so vskill stays usable in CI.
|
|
9
|
+
//
|
|
10
|
+
// This command exists because Claude Code's `claude plugin` CLI has no
|
|
11
|
+
// `new`/`create`/`init` subcommand — plugin authoring is otherwise unowned.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { resolve, join } from "node:path";
|
|
15
|
+
import { pluginJsonScaffold, skillMdScaffold, validateKebabName, } from "../eval-server/authoring-routes.js";
|
|
16
|
+
import { validateClaudePlugin } from "../core/plugin-validator.js";
|
|
17
|
+
import { bold, cyan, dim, green, red, yellow } from "../utils/output.js";
|
|
18
|
+
function exit(code) {
|
|
19
|
+
process.exit(code);
|
|
20
|
+
// satisfies TS in tests where exit is spied
|
|
21
|
+
throw new Error("process.exit did not terminate");
|
|
22
|
+
}
|
|
23
|
+
export async function pluginNewCommand(pluginName, opts) {
|
|
24
|
+
const nameErr = validateKebabName(pluginName, "plugin name");
|
|
25
|
+
if (nameErr) {
|
|
26
|
+
console.error(red("Error: ") + dim(nameErr));
|
|
27
|
+
exit(1);
|
|
28
|
+
}
|
|
29
|
+
const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
30
|
+
const pluginDir = join(cwd, pluginName);
|
|
31
|
+
const manifestDir = join(pluginDir, ".claude-plugin");
|
|
32
|
+
const manifestPath = join(manifestDir, "plugin.json");
|
|
33
|
+
if (existsSync(manifestPath)) {
|
|
34
|
+
console.error(red("Error: ") +
|
|
35
|
+
dim(`plugin manifest already exists: ${manifestPath}`));
|
|
36
|
+
console.error(dim(" Pick a different name or delete the existing manifest first."));
|
|
37
|
+
exit(1);
|
|
38
|
+
}
|
|
39
|
+
let withSkill = null;
|
|
40
|
+
if (opts.withSkill !== undefined) {
|
|
41
|
+
const skillErr = validateKebabName(opts.withSkill, "--with-skill");
|
|
42
|
+
if (skillErr) {
|
|
43
|
+
console.error(red("Error: ") + dim(skillErr));
|
|
44
|
+
exit(1);
|
|
45
|
+
}
|
|
46
|
+
withSkill = opts.withSkill;
|
|
47
|
+
const skillDir = join(pluginDir, "skills", withSkill);
|
|
48
|
+
if (existsSync(skillDir)) {
|
|
49
|
+
console.error(red("Error: ") +
|
|
50
|
+
dim(`skill directory already exists: ${skillDir}`));
|
|
51
|
+
exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const description = (opts.description ?? "").trim() || `Plugin: ${pluginName}`;
|
|
55
|
+
// Write the manifest (and optional first skill) as an all-or-nothing block.
|
|
56
|
+
// If validation fails we roll back the manifest; we leave the (newly
|
|
57
|
+
// created) skill folder in place because deleting user-visible new files we
|
|
58
|
+
// just created is more surprising than leaving them.
|
|
59
|
+
try {
|
|
60
|
+
mkdirSync(manifestDir, { recursive: true });
|
|
61
|
+
writeFileSync(manifestPath, pluginJsonScaffold(pluginName, description), "utf8");
|
|
62
|
+
if (withSkill) {
|
|
63
|
+
const skillDir = join(pluginDir, "skills", withSkill);
|
|
64
|
+
mkdirSync(skillDir, { recursive: true });
|
|
65
|
+
writeFileSync(join(skillDir, "SKILL.md"), skillMdScaffold(withSkill, description), "utf8");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
70
|
+
console.error(red("Error: ") + dim(`failed to write plugin files: ${message}`));
|
|
71
|
+
exit(1);
|
|
72
|
+
}
|
|
73
|
+
// Schema validation via `claude plugin validate` — single source of truth
|
|
74
|
+
// for the plugin schema.
|
|
75
|
+
const validation = validateClaudePlugin(pluginDir);
|
|
76
|
+
if (!validation.ok) {
|
|
77
|
+
try {
|
|
78
|
+
unlinkSync(manifestPath);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
/* best-effort rollback */
|
|
82
|
+
}
|
|
83
|
+
console.error(red("Error: ") + dim("claude plugin validate rejected the generated manifest:"));
|
|
84
|
+
console.error(validation.stderr || "(no stderr captured)");
|
|
85
|
+
console.error(dim(" The manifest has been removed. Please file an issue if this is unexpected."));
|
|
86
|
+
exit(1);
|
|
87
|
+
}
|
|
88
|
+
if (validation.skipped) {
|
|
89
|
+
console.warn(yellow("Warning: ") +
|
|
90
|
+
dim("`claude` CLI not found on PATH; plugin schema was NOT validated."));
|
|
91
|
+
console.warn(dim(" Install Claude Code (https://claude.com/claude-code) to enable validation."));
|
|
92
|
+
}
|
|
93
|
+
// Success summary
|
|
94
|
+
console.log(green("✔ Plugin scaffolded"));
|
|
95
|
+
console.log(dim(" manifest: ") + manifestPath);
|
|
96
|
+
if (withSkill) {
|
|
97
|
+
console.log(dim(" skill: ") + join(pluginDir, "skills", withSkill, "SKILL.md"));
|
|
98
|
+
}
|
|
99
|
+
console.log("");
|
|
100
|
+
console.log(bold("Next steps:"));
|
|
101
|
+
console.log(" " + cyan(`cd ${pluginName}`));
|
|
102
|
+
if (!withSkill) {
|
|
103
|
+
console.log(" " +
|
|
104
|
+
cyan(`vskill skill new --prompt "..."`) +
|
|
105
|
+
dim(" # add your first skill"));
|
|
106
|
+
}
|
|
107
|
+
console.log(" " + cyan(`vskill studio`) + dim(" # open in Skill Studio"));
|
|
108
|
+
}
|
|
109
|
+
export function registerPluginCommand(program) {
|
|
110
|
+
const plugin = program
|
|
111
|
+
.command("plugin")
|
|
112
|
+
.description("Plugin authoring: scaffold a Claude Code plugin (.claude-plugin/plugin.json + skills/)");
|
|
113
|
+
plugin
|
|
114
|
+
.command("new <name>")
|
|
115
|
+
.description("Scaffold a new plugin folder with manifest and (optional) first skill")
|
|
116
|
+
.option("--description <text>", "Description written into plugin.json")
|
|
117
|
+
.option("--with-skill <slug>", "Also scaffold <name>/skills/<slug>/SKILL.md as the first skill")
|
|
118
|
+
.option("--cwd <path>", "Create the plugin under this directory (default: process.cwd())")
|
|
119
|
+
.action(async (name, opts) => {
|
|
120
|
+
await pluginNewCommand(name, opts);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/commands/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface PluginValidationResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
skipped: boolean;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ValidateClaudePluginOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Override the binary name (tests inject a fake "claude").
|
|
9
|
+
*/
|
|
10
|
+
bin?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Timeout in ms. Defaults to 10s — `claude plugin validate` is local-only
|
|
13
|
+
* and typically returns in <300ms; 10s is generous headroom.
|
|
14
|
+
*/
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function validateClaudePlugin(pluginPath: string, opts?: ValidateClaudePluginOptions): PluginValidationResult;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// 0793 — `claude plugin validate <path>` wrapper.
|
|
3
|
+
//
|
|
4
|
+
// vskill never duplicates Claude Code's plugin schema. Both `vskill plugin new`
|
|
5
|
+
// (CLI) and `POST /api/authoring/convert-to-plugin` (Studio) call this helper
|
|
6
|
+
// after writing a manifest to confirm the JSON we wrote conforms. When `claude`
|
|
7
|
+
// is not on PATH (e.g. CI without Claude Code installed), validation soft-skips
|
|
8
|
+
// with `{ ok: true, skipped: true }` so vskill stays usable; callers should
|
|
9
|
+
// surface that state to the user.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
export function validateClaudePlugin(pluginPath, opts = {}) {
|
|
13
|
+
const bin = opts.bin ?? "claude";
|
|
14
|
+
const result = spawnSync(bin, ["plugin", "validate", pluginPath], {
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
timeout: opts.timeoutMs ?? 10_000,
|
|
17
|
+
});
|
|
18
|
+
// ENOENT => `claude` not on PATH. Soft-skip so vskill remains usable in
|
|
19
|
+
// environments without Claude Code (CI, fresh dev boxes, etc).
|
|
20
|
+
// `result.error` is a NodeJS.ErrnoException when spawn itself failed.
|
|
21
|
+
const err = result.error;
|
|
22
|
+
if (err && err.code === "ENOENT") {
|
|
23
|
+
return { ok: true, skipped: true, stderr: "" };
|
|
24
|
+
}
|
|
25
|
+
if (err) {
|
|
26
|
+
// Any other spawn error (timeout, permission, etc): surface but don't
|
|
27
|
+
// treat as schema failure — caller decides what to do.
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
skipped: false,
|
|
31
|
+
stderr: `failed to invoke ${bin}: ${err.message}`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (result.status === 0) {
|
|
35
|
+
return { ok: true, skipped: false, stderr: "" };
|
|
36
|
+
}
|
|
37
|
+
// `claude plugin validate` writes diagnostics to stderr (or stdout in some
|
|
38
|
+
// versions). Concatenate both so callers always see the message.
|
|
39
|
+
const stderr = [result.stderr ?? "", result.stdout ?? ""]
|
|
40
|
+
.filter((s) => s && s.trim().length > 0)
|
|
41
|
+
.join("\n")
|
|
42
|
+
.trim();
|
|
43
|
+
return { ok: false, skipped: false, stderr };
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=plugin-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-validator.js","sourceRoot":"","sources":["../../src/core/plugin-validator.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kDAAkD;AAClD,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,gFAAgF;AAChF,4EAA4E;AAC5E,kCAAkC;AAClC,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAoB/C,MAAM,UAAU,oBAAoB,CAClC,UAAkB,EAClB,OAAoC,EAAE;IAEtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC;IACjC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE;QAChE,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,IAAI,CAAC,SAAS,IAAI,MAAM;KAClC,CAAC,CAAC;IAEH,wEAAwE;IACxE,+DAA+D;IAC/D,sEAAsE;IACtE,MAAM,GAAG,GAAG,MAAM,CAAC,KAA4C,CAAC;IAChE,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACjD,CAAC;IACD,IAAI,GAAG,EAAE,CAAC;QACR,sEAAsE;QACtE,uDAAuD;QACvD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,oBAAoB,GAAG,KAAK,GAAG,CAAC,OAAO,EAAE;SAClD,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAClD,CAAC;IAED,2EAA2E;IAC3E,iEAAiE;IACjE,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;SACtD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;SACvC,IAAI,CAAC,IAAI,CAAC;SACV,IAAI,EAAE,CAAC;IACV,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC/C,CAAC"}
|
|
@@ -951,6 +951,17 @@ function getEffectiveRawModel() {
|
|
|
951
951
|
function getClient() {
|
|
952
952
|
return createLlmClient(currentOverrides);
|
|
953
953
|
}
|
|
954
|
+
// Per-request client: prefer body-supplied provider/model, then session globals.
|
|
955
|
+
// Lets the frontend send the user's selected model with each request so historical
|
|
956
|
+
// runs reliably reflect the picker value rather than a stale session default.
|
|
957
|
+
function clientFromBody(body) {
|
|
958
|
+
const reqProvider = typeof body?.provider === "string" ? body.provider : undefined;
|
|
959
|
+
const reqModel = typeof body?.model === "string" ? body.model : undefined;
|
|
960
|
+
const provider = (reqProvider || currentOverrides.provider || "claude-cli");
|
|
961
|
+
const model = reqModel || currentOverrides.model;
|
|
962
|
+
const client = createLlmClient({ provider, model });
|
|
963
|
+
return { client, provider };
|
|
964
|
+
}
|
|
954
965
|
/** Derive sidebar badge status from benchmark + current eval IDs. */
|
|
955
966
|
function computeBenchmarkStatus(benchmark, evalIds, hasEvals) {
|
|
956
967
|
if (!benchmark)
|
|
@@ -2549,7 +2560,7 @@ export function registerRoutes(router, root, projectName) {
|
|
|
2549
2560
|
const evals = loadAndValidateEvals(skillDir);
|
|
2550
2561
|
const skillMdPath = join(skillDir, "SKILL.md");
|
|
2551
2562
|
const skillContent = existsSync(skillMdPath) ? readFileSync(skillMdPath, "utf-8") : "";
|
|
2552
|
-
const client =
|
|
2563
|
+
const { client, provider: effectiveProvider } = clientFromBody(body);
|
|
2553
2564
|
const systemPrompt = buildEvalSystemPrompt(skillContent);
|
|
2554
2565
|
// Create separate judge client if judgeModel is specified
|
|
2555
2566
|
let judgeClient;
|
|
@@ -2570,7 +2581,7 @@ export function registerRoutes(router, root, projectName) {
|
|
|
2570
2581
|
}
|
|
2571
2582
|
await runBenchmarkSSE({
|
|
2572
2583
|
res, skillDir, skillName: evals.skill_name, systemPrompt,
|
|
2573
|
-
runType: "benchmark", provider:
|
|
2584
|
+
runType: "benchmark", provider: effectiveProvider,
|
|
2574
2585
|
evalCases: evals.evals, filterIds, client, judgeClient, judgeCache,
|
|
2575
2586
|
isAborted: () => aborted, concurrency,
|
|
2576
2587
|
});
|
|
@@ -2592,11 +2603,11 @@ export function registerRoutes(router, root, projectName) {
|
|
|
2592
2603
|
initSSE(res, req);
|
|
2593
2604
|
try {
|
|
2594
2605
|
const evals = loadAndValidateEvals(skillDir);
|
|
2595
|
-
const client =
|
|
2606
|
+
const { client, provider: effectiveProvider } = clientFromBody(body);
|
|
2596
2607
|
await runBenchmarkSSE({
|
|
2597
2608
|
res, skillDir, skillName: evals.skill_name,
|
|
2598
2609
|
systemPrompt: "You are a helpful AI assistant.",
|
|
2599
|
-
runType: "baseline", provider:
|
|
2610
|
+
runType: "baseline", provider: effectiveProvider,
|
|
2600
2611
|
evalCases: evals.evals, filterIds, client, isAborted: () => aborted,
|
|
2601
2612
|
});
|
|
2602
2613
|
}
|
|
@@ -2636,14 +2647,14 @@ export function registerRoutes(router, root, projectName) {
|
|
|
2636
2647
|
}
|
|
2637
2648
|
const skillMdPath = join(skillDir, "SKILL.md");
|
|
2638
2649
|
const skillContent = existsSync(skillMdPath) ? readFileSync(skillMdPath, "utf-8") : "";
|
|
2639
|
-
const client =
|
|
2650
|
+
const { client, provider: effectiveProvider } = clientFromBody(body);
|
|
2640
2651
|
const systemPrompt = isBaseline
|
|
2641
2652
|
? buildBaselineSystemPrompt()
|
|
2642
2653
|
: buildEvalSystemPrompt(skillContent);
|
|
2643
2654
|
await sem.acquire();
|
|
2644
2655
|
const benchCase = await runSingleCaseSSE({
|
|
2645
2656
|
res, evalCase, systemPrompt, client, isAborted: () => aborted,
|
|
2646
|
-
provider:
|
|
2657
|
+
provider: effectiveProvider,
|
|
2647
2658
|
});
|
|
2648
2659
|
if (!released) {
|
|
2649
2660
|
released = true;
|
|
@@ -2659,7 +2670,7 @@ export function registerRoutes(router, root, projectName) {
|
|
|
2659
2670
|
cases: [benchCase],
|
|
2660
2671
|
overall_pass_rate: benchCase.pass_rate,
|
|
2661
2672
|
type: isBaseline ? "baseline" : "benchmark",
|
|
2662
|
-
provider:
|
|
2673
|
+
provider: effectiveProvider,
|
|
2663
2674
|
totalDurationMs: benchCase.durationMs ?? 0,
|
|
2664
2675
|
totalInputTokens: benchCase.inputTokens ?? null,
|
|
2665
2676
|
totalOutputTokens: benchCase.outputTokens ?? null,
|