vskill 0.5.158 → 0.5.159

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "generatedAt": "2026-04-27T06:32:42.566Z",
3
+ "generatedAt": "2026-04-27T07:07:39.600Z",
4
4
  "agentPrefixes": [
5
5
  ".adal",
6
6
  ".agent",
@@ -2297,15 +2297,31 @@ export function registerRoutes(router, root, projectName) {
2297
2297
  sendJson(res, { error: `Failed to delete skill: ${err.message}` }, 500, req);
2298
2298
  }
2299
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.
2300
+ // 0780 / 0786 — Uninstall an installed (lockfile-tracked) skill. Symmetric
2301
+ // to `vskill install`: removes the lockfile entry AND trashes the on-disk
2302
+ // dir.
2304
2303
  //
2305
2304
  // Distinct from DELETE /api/skills/:plugin/:skill (which trashes
2306
2305
  // source-authored skills only and explicitly rejects installed copies):
2307
2306
  // this route is the canonical uninstall path for lockfile-tracked
2308
2307
  // installed skills.
2308
+ //
2309
+ // Response contract (revised by 0786 US-003):
2310
+ // 400 invalid-skill-name — skill name fails kebab-case regex
2311
+ // 400 invalid-path — path traversal attempt
2312
+ // 422 not-installed — lockfile has no entry for this skill
2313
+ // (caller should suggest Delete instead;
2314
+ // see App.tsx pendingUninstall onFailure)
2315
+ // 500 lockfile-read-failed — readLockfile threw
2316
+ // 500 lockfile-write-failed — writeLockfile threw after entry found
2317
+ // 500 trash-failed — OS trash of skill dir threw
2318
+ // 200 ok — `{ removedFromLockfile, trashedDir }`
2319
+ //
2320
+ // The lockfile-first 422 check is the single security-relevant gate that
2321
+ // prevents trashing a source-authored dir at .claude/skills/<name> that
2322
+ // happens to exist but was never installed via the lockfile. The previous
2323
+ // 404 fallback ("neither lockfile entry nor disk dir present") is no longer
2324
+ // reachable and was removed.
2309
2325
  router.post("/api/skills/:plugin/:skill/uninstall", async (req, res, params) => {
2310
2326
  // Skill-name validation — same kebab-case regex used by skill-create
2311
2327
  // routes. Performed BEFORE any filesystem access to defang path-traversal
@@ -2337,14 +2353,14 @@ export function registerRoutes(router, root, projectName) {
2337
2353
  sendJson(res, { error: `Failed to read lockfile: ${err.message}`, code: "lockfile-read-failed" }, 500, req);
2338
2354
  return;
2339
2355
  }
2340
- const lockfileHasEntry = !!(lock && lock.skills && Object.prototype.hasOwnProperty.call(lock.skills, params.skill));
2341
- if (!lockfileHasEntry) {
2356
+ if (!lock || !lock.skills || !Object.prototype.hasOwnProperty.call(lock.skills, params.skill)) {
2342
2357
  sendJson(res, {
2343
2358
  error: `${params.skill} is not installed via the lockfile — uninstall is not applicable. For source-authored skills, use Delete instead.`,
2344
2359
  code: "not-installed",
2345
2360
  }, 422, req);
2346
2361
  return;
2347
2362
  }
2363
+ // Type-narrowed past this point: lock and lock.skills are both non-null.
2348
2364
  let removedFromLockfile = false;
2349
2365
  try {
2350
2366
  delete lock.skills[params.skill];