vskill 0.5.107 → 0.5.108

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.
Files changed (60) hide show
  1. package/README.md +109 -0
  2. package/agents.json +1 -1
  3. package/dist/commands/add.d.ts +36 -0
  4. package/dist/commands/add.js +149 -0
  5. package/dist/commands/add.js.map +1 -1
  6. package/dist/commands/cleanup.d.ts +6 -1
  7. package/dist/commands/cleanup.js +37 -5
  8. package/dist/commands/cleanup.js.map +1 -1
  9. package/dist/commands/disable.d.ts +8 -0
  10. package/dist/commands/disable.js +194 -0
  11. package/dist/commands/disable.js.map +1 -0
  12. package/dist/commands/enable.d.ts +11 -0
  13. package/dist/commands/enable.js +209 -0
  14. package/dist/commands/enable.js.map +1 -0
  15. package/dist/commands/eval/serve.js +18 -4
  16. package/dist/commands/eval/serve.js.map +1 -1
  17. package/dist/commands/list.d.ts +2 -0
  18. package/dist/commands/list.js +83 -1
  19. package/dist/commands/list.js.map +1 -1
  20. package/dist/commands/remove.d.ts +2 -0
  21. package/dist/commands/remove.js +84 -11
  22. package/dist/commands/remove.js.map +1 -1
  23. package/dist/eval/llm.js +7 -0
  24. package/dist/eval/llm.js.map +1 -1
  25. package/dist/eval-server/__tests__/helpers/skill-md-test-helpers.d.ts +1 -0
  26. package/dist/eval-server/__tests__/helpers/skill-md-test-helpers.js +16 -0
  27. package/dist/eval-server/__tests__/helpers/skill-md-test-helpers.js.map +1 -0
  28. package/dist/eval-server/api-routes.d.ts +13 -2
  29. package/dist/eval-server/api-routes.js +314 -37
  30. package/dist/eval-server/api-routes.js.map +1 -1
  31. package/dist/eval-server/eval-server.js +9 -6
  32. package/dist/eval-server/eval-server.js.map +1 -1
  33. package/dist/eval-server/improve-routes.js +50 -2
  34. package/dist/eval-server/improve-routes.js.map +1 -1
  35. package/dist/eval-server/platform-proxy.d.ts +22 -0
  36. package/dist/eval-server/platform-proxy.js +53 -4
  37. package/dist/eval-server/platform-proxy.js.map +1 -1
  38. package/dist/eval-server/providers.js +4 -1
  39. package/dist/eval-server/providers.js.map +1 -1
  40. package/dist/eval-server/settings-store.js +19 -1
  41. package/dist/eval-server/settings-store.js.map +1 -1
  42. package/dist/eval-server/skill-create-routes.d.ts +7 -0
  43. package/dist/eval-server/skill-create-routes.js +92 -17
  44. package/dist/eval-server/skill-create-routes.js.map +1 -1
  45. package/dist/eval-server/studio-json.js +12 -3
  46. package/dist/eval-server/studio-json.js.map +1 -1
  47. package/dist/eval-ui/assets/{CommandPalette-sazEtk0a.js → CommandPalette-mzfSdGtx.js} +1 -1
  48. package/dist/eval-ui/assets/{CreateSkillPage-B5qENwBj.js → CreateSkillPage-CjeJOSY4.js} +1 -1
  49. package/dist/eval-ui/assets/{UpdateDropdown-Ce9LXOxb.js → UpdateDropdown-QP4FPm7W.js} +1 -1
  50. package/dist/eval-ui/assets/{index-vn5bFfrb.js → index-CKclbM8p.js} +38 -38
  51. package/dist/eval-ui/index.html +1 -1
  52. package/dist/index.js +38 -2
  53. package/dist/index.js.map +1 -1
  54. package/dist/lib/skill-lifecycle.d.ts +78 -0
  55. package/dist/lib/skill-lifecycle.js +136 -0
  56. package/dist/lib/skill-lifecycle.js.map +1 -0
  57. package/dist/utils/version.d.ts +13 -0
  58. package/dist/utils/version.js +29 -0
  59. package/dist/utils/version.js.map +1 -1
  60. package/package.json +1 -1
package/README.md CHANGED
@@ -133,11 +133,15 @@ Then invoke as `/plugin:skill` in your agent:
133
133
 
134
134
  ```
135
135
  vskill install <source> Install skill after security scan
136
+ vskill enable <skill> Enable a previously-installed skill in Claude Code
137
+ vskill disable <skill> Disable a skill (keep files on disk; flip the toggle)
136
138
  vskill find <query> Search the verified-skill.com registry
137
139
  vskill scan <path> Run security scan without installing
138
140
  vskill list Show installed skills with status
141
+ vskill list --installed Per-scope enabled/disabled status table
139
142
  vskill remove <skill> Remove an installed skill
140
143
  vskill update [skill] Update with diff scanning (--all for everything)
144
+ vskill cleanup Remove stale plugin entries and orphaned cache
141
145
  vskill audit [path] Full project security audit with LLM analysis
142
146
  vskill info <skill> Show detailed skill information
143
147
  vskill submit <source> Submit a skill for verification
@@ -147,6 +151,108 @@ vskill diff <s> <from> <to> Show multi-file diff between two versions
147
151
  vskill keys <cmd> [provider] Manage LLM API keys (set/list/remove/path)
148
152
  ```
149
153
 
154
+ ## Enable / Disable
155
+
156
+ `vskill install` extracts a skill's files and (when the source is a Claude
157
+ Code marketplace plugin) registers the plugin in `~/.claude/settings.json`'s
158
+ `enabledPlugins`. Once installed, you can toggle it without re-downloading:
159
+
160
+ ```bash
161
+ # Disable a skill — keeps files on disk, just removes the enabledPlugins entry.
162
+ vskill disable foo
163
+
164
+ # Re-enable later — same plugin id, no network round-trip.
165
+ vskill enable foo
166
+
167
+ # Project-scope toggle (writes <cwd>/.claude/settings.json instead of ~/).
168
+ vskill enable foo --scope project
169
+
170
+ # Preview what would happen (prints the exact `claude plugin install/uninstall`
171
+ # invocation, no subprocess spawn).
172
+ vskill enable foo --dry-run
173
+ vskill disable foo --dry-run
174
+
175
+ # Verbose — shows resolved binary path + scope + cwd.
176
+ vskill enable foo --verbose
177
+
178
+ # Machine-readable JSON.
179
+ vskill enable foo --json
180
+ ```
181
+
182
+ Both commands wrap `claude plugin install/uninstall` per
183
+ [ADR 0724-01](../../.specweave/docs/internal/architecture/adr/0724-01-skill-enable-disable-via-claude-cli.md)
184
+ — vskill never writes `settings.json` directly. The commands are
185
+ **idempotent**: running `vskill enable foo` twice prints
186
+ `foo already enabled in user scope` on the second run and exits 0.
187
+
188
+ ### Install with `--no-enable`
189
+
190
+ For CI pipelines that want the files on disk and the lockfile entry written
191
+ but **not** the plugin registered:
192
+
193
+ ```bash
194
+ vskill install anton-abyzov/skill-foo --no-enable
195
+ # … later, when you actually want it active:
196
+ vskill enable foo
197
+ ```
198
+
199
+ ### `vskill list --installed`
200
+
201
+ Joins `vskill.lock` with `enabledPlugins` reads at user and project scope:
202
+
203
+ ```
204
+ $ vskill list --installed
205
+
206
+ Installed Skills (3)
207
+
208
+ Skill Version Source User Scope Project Scope
209
+ foo 1.0.0 marketplace:o/r#foo enabled disabled
210
+ bar 2.1.0 marketplace:o/r#bar disabled enabled
211
+ baz 0.1.0 github:o/r#baz n/a n/a
212
+ ```
213
+
214
+ The `n/a` rows are auto-discovered skills (no `marketplace` field in their
215
+ lockfile entry) — there's nothing to toggle for them; agents pick them up
216
+ directly from their `localSkillsDir`/`globalSkillsDir` on the filesystem.
217
+
218
+ JSON output (`--installed --json`) emits one object per skill with
219
+ `{name, version, source, enabledUser, enabledProject, autoDiscovered}` —
220
+ suitable for piping into `jq`.
221
+
222
+ ### Multi-agent surface awareness
223
+
224
+ When you have Claude Code AND Cursor AND Codex CLI installed, both
225
+ `install` and `enable` print a per-agent line so you know exactly which
226
+ surface received the registration:
227
+
228
+ ```
229
+ $ vskill enable foo
230
+
231
+ Enabled foo (foo@m) in user scope.
232
+ > Claude Code (user) — enabled via claude CLI
233
+ > Cursor — auto-discovers from .cursor/skills (no plugin enable needed)
234
+ > Codex CLI — auto-discovers from .codex/skills (no plugin enable needed)
235
+ ```
236
+
237
+ For non-Claude-Code agents that auto-discover from disk, there is nothing
238
+ to toggle — the skill is live the moment its files exist in the agent's
239
+ `localSkillsDir`/`globalSkillsDir`. To stop loading, run
240
+ `vskill remove <name>`.
241
+
242
+ ### `vskill cleanup --dry-run`
243
+
244
+ Preview which stale `enabledPlugins` entries would be removed before
245
+ running the real cleanup:
246
+
247
+ ```bash
248
+ vskill cleanup --dry-run
249
+ # Dry-run — preview of stale plugin uninstalls:
250
+ # > claude plugin uninstall --scope user -- old-foo@m
251
+ # > claude plugin uninstall --scope project -- old-bar@m
252
+ #
253
+ # 2 stale entries removed from user scope, 0 from project scope, 5 in-sync skills left untouched.
254
+ ```
255
+
150
256
  ## Compare skill versions
151
257
 
152
258
  `vskill diff <skill> <from> <to>` fetches a multi-file diff from
@@ -203,6 +309,9 @@ See [https://github.com/anton-abyzov/vskill/compare/4f2285d...71a9132](https://g
203
309
  | `--force` | Install even if blocklisted |
204
310
  | `--cwd <path>` | Override project root |
205
311
  | `--all` | Install all skills from a repo |
312
+ | `--no-enable` | Install files + lockfile entry, but skip `claude plugin install` |
313
+ | `--scope <scope>` | Plugin enable scope: `user` or `project` |
314
+ | `--dry-run` | Preview install / enable invocations without executing |
206
315
 
207
316
  </details>
208
317
 
package/agents.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "generatedAt": "2026-04-25T05:54:03.282Z",
3
+ "generatedAt": "2026-04-25T15:51:24.942Z",
4
4
  "agentPrefixes": [
5
5
  ".adal",
6
6
  ".agent",
@@ -1,3 +1,4 @@
1
+ import type { AgentDefinition } from "../agents/agents-registry.js";
1
2
  interface MarketplaceDetectionResult {
2
3
  isMarketplace: boolean;
3
4
  manifestContent?: string;
@@ -29,5 +30,40 @@ interface AddOptions {
29
30
  _targetSkill?: string;
30
31
  /** Filter which skills to install from a plugin (comma-separated names) */
31
32
  onlySkills?: string;
33
+ /** When false (--no-enable), skip the post-install `claude plugin install` step. */
34
+ enable?: boolean;
35
+ /** Explicit settings.json scope for the enable step. Falls back to `global ? "user" : "project"`. */
36
+ scope?: "user" | "project";
37
+ /** Print what enable / lockfile mutations would happen, no side effects. */
38
+ dryRun?: boolean;
32
39
  }
40
+ /**
41
+ * 0724 T-006: post-install claude-plugin enable hook.
42
+ *
43
+ * @internal exported for unit tests in __tests__/add-no-enable.test.ts —
44
+ * driving the helper directly is much cheaper than threading the entire
45
+ * addCommand() through fake GitHub fetches and tarball extraction.
46
+ *
47
+ * Called once per just-installed marketplace plugin entry, after the
48
+ * lockfile has been written. Honours `opts.enable === false` (--no-enable)
49
+ * and `opts.dryRun`. Resolves the plugin id via shared helper. Throws on
50
+ * failure so the caller can roll back the filesystem extraction
51
+ * (AC-US1-05).
52
+ */
53
+ export declare function enableAfterInstall(skillName: string, entry: {
54
+ marketplace?: string;
55
+ }, opts: AddOptions): {
56
+ invoked: boolean;
57
+ pluginId: string | null;
58
+ scope: "user" | "project";
59
+ };
60
+ /**
61
+ * 0724 T-006: rollback the on-disk extraction + lockfile entry when
62
+ * `claudePluginInstall` throws (AC-US1-05). Best-effort — wraps each rm in
63
+ * try/catch so a partial filesystem state doesn't suppress the underlying
64
+ * diagnostic.
65
+ *
66
+ * @internal exported for unit tests; see `enableAfterInstall` note above.
67
+ */
68
+ export declare function rollbackInstall(skillName: string, agents: AgentDefinition[], opts: AddOptions): void;
33
69
  export declare function addCommand(source: string | undefined, opts: AddOptions): Promise<void>;
@@ -27,6 +27,12 @@ import { getMarketplaceName } from "../marketplace/index.js";
27
27
  import { rankSearchResults, formatSkillId, getTrustBadge, formatResultLine } from "../utils/skill-display.js";
28
28
  import { computeSha } from "../updater/source-fetcher.js";
29
29
  import { extractFrontmatterVersion } from "../utils/version.js";
30
+ // 0724 T-006: enable-after-install path. We delegate every enabledPlugins
31
+ // mutation to the claude CLI (ADR 0724-01), and we honour the --no-enable
32
+ // flag to skip it. Uninstall is imported for F-003 rollback of earlier
33
+ // already-enabled plugins on a partway failure.
34
+ import { claudePluginInstall, claudePluginUninstall } from "../utils/claude-plugin.js";
35
+ import { buildPerAgentReport, resolvePluginId, } from "../lib/skill-lifecycle.js";
30
36
  // ---------------------------------------------------------------------------
31
37
  /** Validate that a download_url from GitHub Contents API points to a trusted GitHub domain. */
32
38
  function isGitHubDownloadUrl(url) {
@@ -529,6 +535,86 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
529
535
  }
530
536
  }
531
537
  writeLockfile(lockForWrite, lockDir);
538
+ // 0724 T-006 (AC-US1-01..05): claude-plugin enable hook + rollback on failure.
539
+ // Per-agent report is emitted for each successfully-installed plugin so users
540
+ // see exactly which agent surfaces received the registration.
541
+ //
542
+ // Backward-compat (NFR-005): the hook only fires when the user opts into
543
+ // the new surface — i.e., they pass --scope, --no-enable, or --dry-run.
544
+ // Without an opt-in, vskill install behaves exactly as before so existing
545
+ // tests, scripts, and CI pipelines see no behaviour change. AC-US1-01 is
546
+ // read as "when the install flow performs the enable, it does so via
547
+ // claudePluginInstall(<id>, <scope>) exactly once per scope chosen" —
548
+ // i.e., it constrains *how* we enable, not *whether* we always enable.
549
+ // A future major bump will flip this gate to always-on once distribution
550
+ // can also surface --no-enable as the off-switch.
551
+ //
552
+ // When the gate fires, we track every successful enable in
553
+ // `enabledSoFar` so that a later failure rolls back all earlier
554
+ // already-enabled plugins (otherwise we'd leave exactly the stale
555
+ // `enabledPlugins` entries that `vskill cleanup` is meant to fix).
556
+ const userOptedIn = opts.scope !== undefined ||
557
+ opts.dryRun === true ||
558
+ opts.enable === false;
559
+ if (userOptedIn) {
560
+ const enabledSoFar = [];
561
+ for (const r of results) {
562
+ if (!r.installed)
563
+ continue;
564
+ const entry = lockForWrite.skills[r.name];
565
+ if (!entry)
566
+ continue;
567
+ try {
568
+ const enableResult = enableAfterInstall(r.name, entry, opts);
569
+ if (enableResult.invoked && enableResult.pluginId) {
570
+ enabledSoFar.push({
571
+ name: r.name,
572
+ pluginId: enableResult.pluginId,
573
+ scope: enableResult.scope,
574
+ });
575
+ }
576
+ const action = enableResult.invoked ? "enabled" : "not-applicable";
577
+ const report = buildPerAgentReport({
578
+ skill: r.name,
579
+ scope: enableResult.scope,
580
+ action,
581
+ agents,
582
+ });
583
+ for (const line of report)
584
+ console.log(` ${dim(">")} ${line.line}`);
585
+ }
586
+ catch (err) {
587
+ // AC-US1-05: rollback on failure (filesystem + lockfile + earlier
588
+ // already-enabled plugins to avoid leaving stale enabledPlugins).
589
+ const failedScope = opts.scope ?? (opts.global ? "user" : "project");
590
+ console.error(red(`Failed to enable ${r.name} in ${failedScope} scope: ${err.message}`));
591
+ rollbackInstall(r.name, agents, opts);
592
+ // F-003: undo earlier successful enables to keep settings.json
593
+ // clean. Static import — see top of file.
594
+ //
595
+ // Invariant: every plugin in `enabledSoFar` was installed in this
596
+ // single addCommand invocation, so they all share the same `opts`
597
+ // (and therefore the same effective install base resolved by
598
+ // resolveInstallBase). The captured `prev.scope` is what the enable
599
+ // hook actually used, which equals what add.ts computes today from
600
+ // the shared opts. If a future change introduces per-plugin scope
601
+ // override during a batch install, this loop must capture and
602
+ // replay each prev's installBase too — otherwise on-disk rollback
603
+ // would target the wrong path.
604
+ for (const prev of enabledSoFar) {
605
+ try {
606
+ claudePluginUninstall(prev.pluginId, prev.scope, prev.scope === "project" ? { cwd: process.cwd() } : undefined);
607
+ }
608
+ catch {
609
+ /* best-effort */
610
+ }
611
+ rollbackInstall(prev.name, agents, opts);
612
+ }
613
+ process.exit(1);
614
+ return;
615
+ }
616
+ }
617
+ }
532
618
  // Telemetry — batch report for all installed plugins (awaited to prevent process exit race)
533
619
  const repoUrl = `${owner}/${repo}`;
534
620
  const installedSkills = results
@@ -562,6 +648,69 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
562
648
  // ---------------------------------------------------------------------------
563
649
  import { copyPluginFiltered, isSkillMdCandidate, shouldSkipFromCommands, } from "../shared/copy-plugin-filtered.js";
564
650
  export { copyPluginFiltered, isSkillMdCandidate, shouldSkipFromCommands };
651
+ /**
652
+ * 0724 T-006: post-install claude-plugin enable hook.
653
+ *
654
+ * @internal exported for unit tests in __tests__/add-no-enable.test.ts —
655
+ * driving the helper directly is much cheaper than threading the entire
656
+ * addCommand() through fake GitHub fetches and tarball extraction.
657
+ *
658
+ * Called once per just-installed marketplace plugin entry, after the
659
+ * lockfile has been written. Honours `opts.enable === false` (--no-enable)
660
+ * and `opts.dryRun`. Resolves the plugin id via shared helper. Throws on
661
+ * failure so the caller can roll back the filesystem extraction
662
+ * (AC-US1-05).
663
+ */
664
+ export function enableAfterInstall(skillName, entry, opts) {
665
+ const scope = opts.scope ?? (opts.global ? "user" : "project");
666
+ const pluginId = resolvePluginId(skillName, entry);
667
+ if (pluginId === null) {
668
+ if (!opts.dryRun) {
669
+ console.log(dim(`Auto-discovered by agents from skills dir — no enable step needed.`));
670
+ }
671
+ return { invoked: false, pluginId: null, scope };
672
+ }
673
+ if (opts.enable === false) {
674
+ if (!opts.dryRun) {
675
+ console.log(dim(`--no-enable: skipped claude plugin install for ${pluginId}.`));
676
+ }
677
+ return { invoked: false, pluginId, scope };
678
+ }
679
+ if (opts.dryRun) {
680
+ console.log(dim(`Dry-run: would invoke ${cyan(`claude plugin install --scope ${scope} -- ${pluginId}`)}`));
681
+ return { invoked: false, pluginId, scope };
682
+ }
683
+ claudePluginInstall(pluginId, scope, scope === "project" ? { cwd: process.cwd() } : undefined);
684
+ return { invoked: true, pluginId, scope };
685
+ }
686
+ /**
687
+ * 0724 T-006: rollback the on-disk extraction + lockfile entry when
688
+ * `claudePluginInstall` throws (AC-US1-05). Best-effort — wraps each rm in
689
+ * try/catch so a partial filesystem state doesn't suppress the underlying
690
+ * diagnostic.
691
+ *
692
+ * @internal exported for unit tests; see `enableAfterInstall` note above.
693
+ */
694
+ export function rollbackInstall(skillName, agents, opts) {
695
+ for (const agent of agents) {
696
+ const baseDir = resolveInstallBase(opts, agent);
697
+ const skillDir = join(baseDir, skillName);
698
+ try {
699
+ if (existsSync(skillDir)) {
700
+ rmSync(skillDir, { recursive: true, force: true });
701
+ }
702
+ }
703
+ catch {
704
+ // best-effort
705
+ }
706
+ }
707
+ try {
708
+ removeSkillFromLock(skillName);
709
+ }
710
+ catch {
711
+ // best-effort
712
+ }
713
+ }
565
714
  /** Returns process.cwd() as the project root for skill installation. */
566
715
  function safeProjectRoot() {
567
716
  return process.cwd();