vskill 0.5.144 → 0.5.146
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/eval-server/api-routes.js +76 -9
- package/dist/eval-server/api-routes.js.map +1 -1
- package/dist/eval-ui/assets/{CommandPalette-CEp1wNr0.js → CommandPalette-DscfVKzd.js} +1 -1
- package/dist/eval-ui/assets/{CreateSkillPage-DZSf85Db.js → CreateSkillPage-D57vghOb.js} +1 -1
- package/dist/eval-ui/assets/{FindSkillsPalette-CvN0J9-i.js → FindSkillsPalette-CTkqTe9D.js} +2 -2
- package/dist/eval-ui/assets/{SearchPaletteCore-BLRubqQS.js → SearchPaletteCore-DaLRDx2r.js} +1 -1
- package/dist/eval-ui/assets/{SkillDetailPanel-Bb0NxXCs.js → SkillDetailPanel-DuHlzfsV.js} +1 -1
- package/dist/eval-ui/assets/{UpdateDropdown-Bl3VyBRb.js → UpdateDropdown-DPSKxAQn.js} +1 -1
- package/dist/eval-ui/assets/{index-5lXKRaDO.js → index-Dl3yRvTv.js} +41 -41
- package/dist/eval-ui/index.html +1 -1
- package/package.json +1 -1
package/agents.json
CHANGED
|
@@ -9,7 +9,7 @@ import { sendJson, readBody } from "./router.js";
|
|
|
9
9
|
import { initSSE, sendSSE, sendSSEDone, withHeartbeat, startDynamicHeartbeat } from "./sse-helpers.js";
|
|
10
10
|
import { dataEventBus, emitDataEvent } from "./data-events.js";
|
|
11
11
|
import { classifyError } from "./error-classifier.js";
|
|
12
|
-
import { readLockfile } from "../lockfile/lockfile.js";
|
|
12
|
+
import { readLockfile, writeLockfile } from "../lockfile/lockfile.js";
|
|
13
13
|
import { resolveSkillApiName as resolveSkillApiNameImpl } from "./skill-name-resolver.js";
|
|
14
14
|
import { runBenchmarkSSE, runSingleCaseSSE } from "./benchmark-runner.js";
|
|
15
15
|
import { getSkillSemaphore } from "./concurrency.js";
|
|
@@ -824,22 +824,30 @@ function resolveSourceLink(skillDir, root) {
|
|
|
824
824
|
};
|
|
825
825
|
}
|
|
826
826
|
// Legacy derivation from `source: github:owner/repo`.
|
|
827
|
-
// 0743: We DO NOT default `skillPath` to "SKILL.md" here. Multi-skill
|
|
828
|
-
// (vskill, marketingskills, etc.) hold the SKILL.md under a nested
|
|
829
|
-
// and the legacy `source` string carries no path information.
|
|
830
|
-
// "SKILL.md" produced confidently-wrong 404 anchors for every
|
|
831
|
-
// a multi-skill repo.
|
|
832
|
-
//
|
|
833
|
-
//
|
|
827
|
+
// 0743: We DO NOT blindly default `skillPath` to "SKILL.md" here. Multi-skill
|
|
828
|
+
// repos (vskill, marketingskills, etc.) hold the SKILL.md under a nested
|
|
829
|
+
// path, and the legacy `source` string carries no path information.
|
|
830
|
+
// Guessing "SKILL.md" produced confidently-wrong 404 anchors for every
|
|
831
|
+
// install from a multi-skill repo.
|
|
832
|
+
//
|
|
833
|
+
// 0773 hotfix: when the matched lockfile entry KEY equals the source repo
|
|
834
|
+
// basename (i.e. `vskill install anton-abyzov/greet-anton` keys the entry
|
|
835
|
+
// as `greet-anton` AND the repo is `greet-anton`), the repo IS the skill —
|
|
836
|
+
// SKILL.md sits at the repo root. Defaulting skillPath to "SKILL.md" in
|
|
837
|
+
// that exact shape restores the working SourceFileLink anchor for
|
|
838
|
+
// single-skill repos without re-introducing 404s for multi-skill repos.
|
|
834
839
|
const m = /^github:([^/]+)\/([^/#]+)/.exec(entry.source ?? "");
|
|
835
840
|
// 0770: do NOT fall through here — an installed skill with a non-github
|
|
836
841
|
// `source` (e.g. `marketplace:...`) is still installed, not authored. Local
|
|
837
842
|
// git detection would leak the workspace remote (umbrella, etc.).
|
|
838
843
|
if (!m)
|
|
839
844
|
return { repoUrl: null, skillPath: null };
|
|
845
|
+
const repoBasename = m[2];
|
|
846
|
+
const lockKey = lock.skills[skillName] ? skillName : parentName;
|
|
847
|
+
const isSingleSkillRepo = repoBasename === lockKey;
|
|
840
848
|
return {
|
|
841
849
|
repoUrl: `https://github.com/${m[1]}/${m[2]}`,
|
|
842
|
-
skillPath: entry.sourceSkillPath ?? null,
|
|
850
|
+
skillPath: entry.sourceSkillPath ?? (isSingleSkillRepo ? "SKILL.md" : null),
|
|
843
851
|
};
|
|
844
852
|
}
|
|
845
853
|
/**
|
|
@@ -2289,6 +2297,65 @@ export function registerRoutes(router, root, projectName) {
|
|
|
2289
2297
|
sendJson(res, { error: `Failed to delete skill: ${err.message}` }, 500, req);
|
|
2290
2298
|
}
|
|
2291
2299
|
});
|
|
2300
|
+
// 0780 — Uninstall an installed (lockfile-tracked) skill. Symmetric to
|
|
2301
|
+
// `vskill install`: removes the lockfile entry AND trashes the on-disk
|
|
2302
|
+
// dir. Idempotent on partially-removed installs (lockfile entry without
|
|
2303
|
+
// disk dir, or vice versa). Returns 404 only when neither exists.
|
|
2304
|
+
//
|
|
2305
|
+
// Distinct from DELETE /api/skills/:plugin/:skill (which trashes
|
|
2306
|
+
// source-authored skills only and explicitly rejects installed copies):
|
|
2307
|
+
// this route is the canonical uninstall path for lockfile-tracked
|
|
2308
|
+
// installed skills.
|
|
2309
|
+
router.post("/api/skills/:plugin/:skill/uninstall", async (req, res, params) => {
|
|
2310
|
+
// Skill-name validation — same kebab-case regex used by skill-create
|
|
2311
|
+
// routes. Performed BEFORE any filesystem access to defang path-traversal
|
|
2312
|
+
// attempts (e.g. `../../etc/passwd`).
|
|
2313
|
+
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(params.skill)) {
|
|
2314
|
+
sendJson(res, { error: "Invalid skill name", code: "invalid-skill-name" }, 400, req);
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const skillDir = join(root, ".claude", "skills", params.skill);
|
|
2318
|
+
const resolvedDir = resolve(skillDir);
|
|
2319
|
+
const resolvedRoot = resolve(root);
|
|
2320
|
+
if (!resolvedDir.startsWith(resolvedRoot + "/") && resolvedDir !== resolvedRoot) {
|
|
2321
|
+
sendJson(res, { error: "Invalid skill path", code: "invalid-path" }, 400, req);
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
let removedFromLockfile = false;
|
|
2325
|
+
try {
|
|
2326
|
+
const lock = readLockfile(root);
|
|
2327
|
+
if (lock && lock.skills && Object.prototype.hasOwnProperty.call(lock.skills, params.skill)) {
|
|
2328
|
+
delete lock.skills[params.skill];
|
|
2329
|
+
writeLockfile(lock, root);
|
|
2330
|
+
removedFromLockfile = true;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
catch (err) {
|
|
2334
|
+
sendJson(res, { error: `Failed to update lockfile: ${err.message}`, code: "lockfile-write-failed" }, 500, req);
|
|
2335
|
+
return;
|
|
2336
|
+
}
|
|
2337
|
+
let trashedDir = null;
|
|
2338
|
+
if (existsSync(skillDir)) {
|
|
2339
|
+
try {
|
|
2340
|
+
const trash = (await import("trash")).default;
|
|
2341
|
+
await trash([skillDir]);
|
|
2342
|
+
trashedDir = skillDir;
|
|
2343
|
+
}
|
|
2344
|
+
catch (err) {
|
|
2345
|
+
sendJson(res, {
|
|
2346
|
+
error: `Failed to trash skill dir: ${err.message}`,
|
|
2347
|
+
code: "trash-failed",
|
|
2348
|
+
removedFromLockfile,
|
|
2349
|
+
}, 500, req);
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
if (!removedFromLockfile && trashedDir === null) {
|
|
2354
|
+
sendJson(res, { error: "Skill is not installed", code: "not-installed" }, 404, req);
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
sendJson(res, { ok: true, removedFromLockfile, trashedDir }, 200, req);
|
|
2358
|
+
});
|
|
2292
2359
|
// Get skill description (for activation testing preview)
|
|
2293
2360
|
router.get("/api/skills/:plugin/:skill/description", async (req, res, params) => {
|
|
2294
2361
|
const skillDir = resolveSkillDir(root, params.plugin, params.skill);
|