vskill 0.5.138 → 0.5.140

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 (26) hide show
  1. package/README.md +16 -16
  2. package/agents.json +1 -1
  3. package/dist/commands/stamp-versions.d.ts +25 -0
  4. package/dist/commands/stamp-versions.js +153 -0
  5. package/dist/commands/stamp-versions.js.map +1 -0
  6. package/dist/eval-server/api-routes.js +13 -5
  7. package/dist/eval-server/api-routes.js.map +1 -1
  8. package/dist/eval-server/skill-dir-registry.d.ts +15 -0
  9. package/dist/eval-server/skill-dir-registry.js +34 -0
  10. package/dist/eval-server/skill-dir-registry.js.map +1 -1
  11. package/dist/eval-server/skill-resolver.js +6 -1
  12. package/dist/eval-server/skill-resolver.js.map +1 -1
  13. package/dist/eval-ui/assets/{CommandPalette-DWl6kb7K.js → CommandPalette-DeEo1aM2.js} +1 -1
  14. package/dist/eval-ui/assets/{CreateSkillPage-IndkXdwH.js → CreateSkillPage-BGi_wZ1y.js} +1 -1
  15. package/dist/eval-ui/assets/{FindSkillsPalette-Br5cToMQ.js → FindSkillsPalette-BbJJSef2.js} +2 -2
  16. package/dist/eval-ui/assets/{SearchPaletteCore-usZJoJGb.js → SearchPaletteCore-BK0cFJb6.js} +1 -1
  17. package/dist/eval-ui/assets/{SkillDetailPanel-ZGSkm510.js → SkillDetailPanel-DxidXsNP.js} +1 -1
  18. package/dist/eval-ui/assets/{UpdateDropdown-Borwxx95.js → UpdateDropdown-CcaDVKoz.js} +1 -1
  19. package/dist/eval-ui/assets/index-ByzTygib.css +1 -0
  20. package/dist/eval-ui/assets/index-C12VKBb6.js +110 -0
  21. package/dist/eval-ui/index.html +2 -2
  22. package/dist/index.js +16 -0
  23. package/dist/index.js.map +1 -1
  24. package/package.json +1 -1
  25. package/dist/eval-ui/assets/index-BOR1gyX9.js +0 -102
  26. package/dist/eval-ui/assets/index-C8DXCPPg.css +0 -1
package/README.md CHANGED
@@ -18,8 +18,8 @@
18
18
  <br/>
19
19
 
20
20
  ```bash
21
- npx vskill studio # open the local IDE for AI skills
22
- npx vskill install remotion-best-practices
21
+ npx vskill@latest studio # open the local IDE for AI skills
22
+ npx vskill@latest install remotion-best-practices
23
23
  ```
24
24
 
25
25
  <br/>
@@ -40,7 +40,7 @@ vskill is built around three problems:
40
40
  ## Skill Studio — the local IDE for skills
41
41
 
42
42
  ```bash
43
- npx vskill studio
43
+ npx vskill@latest studio
44
44
  ```
45
45
 
46
46
  A localhost workbench opens at a deterministic per-project port. You can:
@@ -77,13 +77,13 @@ Every install runs the full pipeline. SARIF v2.1.0 output is available for CI (`
77
77
 
78
78
  ```bash
79
79
  # Install one skill
80
- npx vskill install remotion-dev/skills/remotion-best-practices
80
+ npx vskill@latest install remotion-dev/skills/remotion-best-practices
81
81
 
82
82
  # Browse a repo, pick interactively
83
- npx vskill install remotion-dev/skills
83
+ npx vskill@latest install remotion-dev/skills
84
84
 
85
85
  # Install a Claude Code plugin (full domain bundle)
86
- npx vskill install --repo anton-abyzov/vskill --plugin frontend
86
+ npx vskill@latest install --repo anton-abyzov/vskill --plugin frontend
87
87
  ```
88
88
 
89
89
  Install globally for repeat use: `npm i -g vskill`
@@ -115,7 +115,7 @@ Install globally for repeat use: `npm i -g vskill`
115
115
  | **easychamp** | tournament-manager |
116
116
  | **productivity** | survey-passing |
117
117
 
118
- Install all eight: `npx vskill install --repo anton-abyzov/vskill --all`
118
+ Install all eight: `npx vskill@latest install --repo anton-abyzov/vskill --all`
119
119
 
120
120
  Browse the full catalog → [verified-skill.com/docs/plugins](https://verified-skill.com/docs/plugins)
121
121
 
@@ -124,15 +124,15 @@ Browse the full catalog → [verified-skill.com/docs/plugins](https://verified-s
124
124
  ## Commands you'll actually use
125
125
 
126
126
  ```bash
127
- vskill studio # open local IDE
128
- vskill install <skill-or-plugin> # install with full security scan
129
- vskill find <query> # search verified-skill.com registry
130
- vskill list --installed # what's installed where
131
- vskill diff <skill> v1 v2 # compare versions before upgrading
132
- vskill skill new # create a new skill (AI-assisted)
133
- vskill eval sweep <skill> # benchmark across models
134
- vskill audit --ci # SARIF v2.1.0 for CI
135
- vskill keys set anthropic # store API keys in ~/.vskill/keys.env
127
+ npx vskill@latest studio # open local IDE
128
+ npx vskill@latest install <skill> # install with full security scan
129
+ npx vskill@latest find <query> # search verified-skill.com registry
130
+ npx vskill@latest list --installed # what's installed where
131
+ npx vskill@latest diff <skill> v1 v2 # compare versions before upgrading
132
+ npx vskill@latest skill new # create a new skill (AI-assisted)
133
+ npx vskill@latest eval sweep <skill> # benchmark across models
134
+ npx vskill@latest audit --ci # SARIF v2.1.0 for CI
135
+ npx vskill@latest keys set anthropic # store API keys in ~/.vskill/keys.env
136
136
  ```
137
137
 
138
138
  Full reference → [verified-skill.com/docs/cli-reference](https://verified-skill.com/docs/cli-reference)
package/agents.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "generatedAt": "2026-04-26T17:43:52.006Z",
3
+ "generatedAt": "2026-04-26T18:39:18.872Z",
4
4
  "agentPrefixes": [
5
5
  ".adal",
6
6
  ".agent",
@@ -0,0 +1,25 @@
1
+ interface StampOptions {
2
+ /** Roots to walk. When omitted, defaults to ~/.claude/skills + ~/.claude/plugins/cache. */
3
+ root?: string[];
4
+ /** Default version to stamp when missing. */
5
+ version?: string;
6
+ /** When true, write changes to disk; otherwise dry-run (just report). */
7
+ write?: boolean;
8
+ /** When true, emit one path per line instead of human report. */
9
+ json?: boolean;
10
+ }
11
+ interface FileResult {
12
+ path: string;
13
+ action: "stamped" | "skipped-has-version" | "skipped-no-frontmatter" | "error";
14
+ current?: string | null;
15
+ next?: string;
16
+ error?: string;
17
+ }
18
+ export declare function stampVersionsCommand(opts?: StampOptions): Promise<{
19
+ scanned: number;
20
+ stamped: number;
21
+ alreadyHadVersion: number;
22
+ errors: number;
23
+ results: FileResult[];
24
+ }>;
25
+ export {};
@@ -0,0 +1,153 @@
1
+ // ---------------------------------------------------------------------------
2
+ // 0759 Phase 7 (B1) — `vskill stamp-versions` command.
3
+ //
4
+ // Walks local skill installation roots and injects `version: "1.0.0"` into the
5
+ // frontmatter of any SKILL.md that is missing a version field. Opt-in only —
6
+ // never runs automatically. Defaults to dry-run; pass `--write` to actually
7
+ // modify files.
8
+ //
9
+ // Default scopes (when no --root passed):
10
+ // - ~/.claude/skills/ (Claude Code personal skills)
11
+ // - ~/.claude/plugins/cache/*/skills/ (plugin-cached skills)
12
+ //
13
+ // Skips files whose frontmatter already declares a version (idempotent).
14
+ // Skips files outside the user's home dir as a safety check.
15
+ // ---------------------------------------------------------------------------
16
+ import { promises as fs } from "node:fs";
17
+ import { homedir } from "node:os";
18
+ import { join, resolve, sep } from "node:path";
19
+ const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---(?:\n|$)/;
20
+ const VERSION_LINE_RE = /^version:\s*["']?([^"'\n]+?)["']?\s*$/m;
21
+ async function findSkillMdFiles(roots) {
22
+ const found = [];
23
+ for (const root of roots) {
24
+ try {
25
+ await walk(root, found);
26
+ }
27
+ catch {
28
+ // Missing root — silently skip.
29
+ }
30
+ }
31
+ return found;
32
+ }
33
+ async function walk(dir, out) {
34
+ let entries;
35
+ try {
36
+ entries = await fs.readdir(dir, { withFileTypes: true });
37
+ }
38
+ catch {
39
+ return;
40
+ }
41
+ for (const entry of entries) {
42
+ const full = join(dir, entry.name);
43
+ if (entry.isSymbolicLink())
44
+ continue;
45
+ if (entry.isDirectory()) {
46
+ await walk(full, out);
47
+ }
48
+ else if (entry.isFile() && entry.name === "SKILL.md") {
49
+ out.push(full);
50
+ }
51
+ }
52
+ }
53
+ function injectVersion(content, version) {
54
+ const versionLine = `version: "${version}"`;
55
+ const fm = content.match(FRONTMATTER_RE);
56
+ if (!fm) {
57
+ // No frontmatter at all — prepend a minimal one.
58
+ return `---\n${versionLine}\n---\n${content}`;
59
+ }
60
+ if (VERSION_LINE_RE.test(fm[1])) {
61
+ return content; // already has version → no-op
62
+ }
63
+ const newBlock = `${versionLine}\n${fm[1]}`;
64
+ const closer = fm[0].endsWith("---\n") ? "---\n" : "---";
65
+ const before = content.slice(0, fm.index ?? 0);
66
+ const after = content.slice((fm.index ?? 0) + fm[0].length);
67
+ return `${before}---\n${newBlock}\n${closer}${after}`;
68
+ }
69
+ export async function stampVersionsCommand(opts = {}) {
70
+ const home = homedir();
71
+ const defaultRoots = [
72
+ join(home, ".claude", "skills"),
73
+ join(home, ".claude", "plugins", "cache"),
74
+ ];
75
+ const roots = (opts.root && opts.root.length > 0 ? opts.root : defaultRoots).map((r) => resolve(r));
76
+ const version = opts.version ?? "1.0.0";
77
+ const write = !!opts.write;
78
+ // Safety: refuse to walk a root that isn't under $HOME (prevents ./. or /).
79
+ const safeRoots = [];
80
+ for (const r of roots) {
81
+ if (r === home || r === resolve(home)) {
82
+ // Refuse a literal home root — would scan EVERYTHING.
83
+ console.error(`vskill stamp-versions: refusing to walk full home dir: ${r}`);
84
+ continue;
85
+ }
86
+ if (!r.startsWith(home + sep)) {
87
+ console.error(`vskill stamp-versions: refusing to walk outside home: ${r}`);
88
+ continue;
89
+ }
90
+ safeRoots.push(r);
91
+ }
92
+ const files = await findSkillMdFiles(safeRoots);
93
+ const results = [];
94
+ let stamped = 0;
95
+ let already = 0;
96
+ let errors = 0;
97
+ for (const path of files) {
98
+ try {
99
+ const content = await fs.readFile(path, "utf8");
100
+ const fm = content.match(FRONTMATTER_RE);
101
+ if (!fm) {
102
+ // No frontmatter — we'd be creating one. That's invasive; skip in dry-run
103
+ // by default and only act on existing-frontmatter cases for stamp-versions.
104
+ // The full publish flow stamps unconditionally; this CLI is conservative.
105
+ results.push({ path, action: "skipped-no-frontmatter" });
106
+ continue;
107
+ }
108
+ if (VERSION_LINE_RE.test(fm[1])) {
109
+ const m = fm[1].match(VERSION_LINE_RE);
110
+ results.push({ path, action: "skipped-has-version", current: m?.[1] ?? null });
111
+ already++;
112
+ continue;
113
+ }
114
+ const next = injectVersion(content, version);
115
+ if (write) {
116
+ await fs.writeFile(path, next, "utf8");
117
+ }
118
+ results.push({ path, action: "stamped", next: version });
119
+ stamped++;
120
+ }
121
+ catch (err) {
122
+ const msg = err instanceof Error ? err.message : String(err);
123
+ results.push({ path, action: "error", error: msg });
124
+ errors++;
125
+ }
126
+ }
127
+ // Reporting
128
+ if (opts.json) {
129
+ process.stdout.write(JSON.stringify({ scanned: files.length, stamped, alreadyHadVersion: already, errors, results, dryRun: !write, version }, null, 2) + "\n");
130
+ }
131
+ else {
132
+ const dryNote = write ? "(applied)" : "(dry-run — pass --write to apply)";
133
+ process.stdout.write(`vskill stamp-versions ${dryNote}\n`);
134
+ process.stdout.write(` scanned: ${files.length}\n`);
135
+ process.stdout.write(` stamped: ${stamped} (with version "${version}")\n`);
136
+ process.stdout.write(` already had version: ${already}\n`);
137
+ process.stdout.write(` errors: ${errors}\n`);
138
+ if (stamped > 0) {
139
+ process.stdout.write(`\nFiles ${write ? "stamped" : "would stamp"}:\n`);
140
+ for (const r of results.filter((r) => r.action === "stamped")) {
141
+ process.stdout.write(` + ${r.path}\n`);
142
+ }
143
+ }
144
+ if (errors > 0) {
145
+ process.stdout.write(`\nErrors:\n`);
146
+ for (const r of results.filter((r) => r.action === "error")) {
147
+ process.stdout.write(` ! ${r.path}: ${r.error}\n`);
148
+ }
149
+ }
150
+ }
151
+ return { scanned: files.length, stamped, alreadyHadVersion: already, errors, results };
152
+ }
153
+ //# sourceMappingURL=stamp-versions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamp-versions.js","sourceRoot":"","sources":["../../src/commands/stamp-versions.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,7 @@ import { resolveSkillApiName as resolveSkillApiNameImpl } from "./skill-name-res
14
14
  import { runBenchmarkSSE, runSingleCaseSSE } from "./benchmark-runner.js";
15
15
  import { getSkillSemaphore } from "./concurrency.js";
16
16
  import { resolveSkillDir, resolveAllowedSkillDir } from "./skill-resolver.js";
17
- import { setSkillDirEntry } from "./skill-dir-registry.js";
17
+ import { setSkillDirEntry, ensurePluginCacheEntry } from "./skill-dir-registry.js";
18
18
  import { pickInstalledVersion, readSkillMd, sha256Hex, } from "./installed-version.js";
19
19
  import { extractFrontmatterVersion } from "../utils/version.js";
20
20
  import { classifyOrigin, scanSkillsTriScope, dedupeByDir } from "../eval/skill-scanner.js";
@@ -1876,12 +1876,20 @@ export function registerRoutes(router, root, projectName) {
1876
1876
  join(home, ".claude/skills"),
1877
1877
  ];
1878
1878
  };
1879
- const resolveSkillDirForFsRoute = (plugin, skill) => resolveAllowedSkillDir(root, plugin, skill, skillFsAllowedRoots());
1879
+ // 0769 F-002: cold-server deep links to /api/skills/:plugin/:skill/files (or
1880
+ // /file) hit the route before the user has visited /api/skills, so the
1881
+ // SkillDirRegistry hasn't been populated yet. Lazy-fill it on registry miss
1882
+ // by running scanInstalledPluginSkills once. Subsequent calls hit the fast
1883
+ // path via setSkillDirEntry inside ensurePluginCacheEntry.
1884
+ const resolveSkillDirForFsRoute = async (plugin, skill) => {
1885
+ await ensurePluginCacheEntry(plugin, skill);
1886
+ return resolveAllowedSkillDir(root, plugin, skill, skillFsAllowedRoots());
1887
+ };
1880
1888
  // Get skill detail
1881
1889
  router.get("/api/skills/:plugin/:skill", async (req, res, params) => {
1882
1890
  let skillDir;
1883
1891
  try {
1884
- skillDir = resolveSkillDirForFsRoute(params.plugin, params.skill);
1892
+ skillDir = await resolveSkillDirForFsRoute(params.plugin, params.skill);
1885
1893
  }
1886
1894
  catch (err) {
1887
1895
  sendJson(res, { error: err.message }, 400, req);
@@ -1899,7 +1907,7 @@ export function registerRoutes(router, root, projectName) {
1899
1907
  router.get("/api/skills/:plugin/:skill/files", async (req, res, params) => {
1900
1908
  let skillDir;
1901
1909
  try {
1902
- skillDir = resolveSkillDirForFsRoute(params.plugin, params.skill);
1910
+ skillDir = await resolveSkillDirForFsRoute(params.plugin, params.skill);
1903
1911
  }
1904
1912
  catch (err) {
1905
1913
  sendJson(res, { error: err.message }, 400, req);
@@ -1960,7 +1968,7 @@ export function registerRoutes(router, root, projectName) {
1960
1968
  router.get("/api/skills/:plugin/:skill/file", async (req, res, params) => {
1961
1969
  let skillDir;
1962
1970
  try {
1963
- skillDir = resolveSkillDirForFsRoute(params.plugin, params.skill);
1971
+ skillDir = await resolveSkillDirForFsRoute(params.plugin, params.skill);
1964
1972
  }
1965
1973
  catch (err) {
1966
1974
  sendJson(res, { error: err.message }, 400, req);