vet-skills 0.5.0 → 0.5.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.
Files changed (2) hide show
  1. package/dist/index.js +66 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7616,7 +7616,7 @@ import { parseArgs } from "node:util";
7616
7616
  import { spawnSync } from "node:child_process";
7617
7617
  import { readFileSync as readFileSync3, existsSync as existsSync4 } from "node:fs";
7618
7618
  import { resolve, join as join4, dirname, basename } from "node:path";
7619
- import { hostname, platform, userInfo } from "node:os";
7619
+ import { hostname, platform as platform2, userInfo } from "node:os";
7620
7620
  import { createHash } from "node:crypto";
7621
7621
  import { createInterface } from "node:readline";
7622
7622
 
@@ -7629,7 +7629,7 @@ import { mkdirSync, readFileSync, writeFileSync, existsSync, appendFileSync, chm
7629
7629
  import { homedir } from "node:os";
7630
7630
  import { join } from "node:path";
7631
7631
  import { randomUUID } from "node:crypto";
7632
- var VERSION = "0.5.0";
7632
+ var VERSION = "0.5.2";
7633
7633
  var HOME = process.env.VET_SKILLS_HOME ?? join(homedir(), ".vet-skills");
7634
7634
  var API = (process.env.KILLERSKILLS_API ?? "https://killerskills.ai").replace(/\/$/, "");
7635
7635
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -7735,6 +7735,33 @@ function stateSummary() {
7735
7735
  const sz = (f) => existsSync(join(HOME, f)) ? statSync(join(HOME, f)).size : 0;
7736
7736
  return { home: HOME, cacheEntries: cacheSize(), cacheBytes: sz("cache.json"), logBytes: sz("log.jsonl") };
7737
7737
  }
7738
+ function telemetryEnabled() {
7739
+ const env = (process.env.VET_SKILLS_TELEMETRY ?? "").toLowerCase();
7740
+ if (["0", "false", "off", "no"].includes(env)) return false;
7741
+ if (process.env.DO_NOT_TRACK === "1") return false;
7742
+ const p = join(HOME, "telemetry");
7743
+ if (existsSync(p)) return readFileSync(p, "utf8").trim() !== "off";
7744
+ return true;
7745
+ }
7746
+ function setTelemetry(on2) {
7747
+ ensureHome();
7748
+ writeFileSync(join(HOME, "telemetry"), on2 ? "on\n" : "off\n", { mode: 384 });
7749
+ }
7750
+ function telemetryNoticePending() {
7751
+ ensureHome();
7752
+ const p = join(HOME, "telemetry-notice");
7753
+ if (existsSync(p)) return false;
7754
+ writeFileSync(p, (/* @__PURE__ */ new Date()).toISOString() + "\n", { mode: 384 });
7755
+ return true;
7756
+ }
7757
+ function hintOncePerDay(key) {
7758
+ ensureHome();
7759
+ const p = join(HOME, `hint-${key.replace(/[^\w-]/g, "_")}`);
7760
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7761
+ if (existsSync(p) && readFileSync(p, "utf8").trim() === today) return false;
7762
+ writeFileSync(p, today + "\n", { mode: 384 });
7763
+ return true;
7764
+ }
7738
7765
 
7739
7766
  // src/api.ts
7740
7767
  async function call(path, init = {}, timeoutMs = 8e3) {
@@ -7792,6 +7819,12 @@ function detonate(tgz, opts = {}) {
7792
7819
  const q = new URLSearchParams();
7793
7820
  return call(`/api/v1/detonate?${q}`, { method: "POST", body: new Blob([tgz]), headers: { "content-type": "application/gzip", ...opts.publisher ? { "x-vs-publisher": opts.publisher } : {} } }, 18e4);
7794
7821
  }
7822
+ function sendTelemetry(body) {
7823
+ return call("/api/v1/telemetry", { method: "POST", body: JSON.stringify(body) }, 1500);
7824
+ }
7825
+
7826
+ // src/check.ts
7827
+ import { platform } from "node:os";
7795
7828
 
7796
7829
  // src/render.ts
7797
7830
  var on = !!process.stdout.isTTY && !process.env.NO_COLOR;
@@ -7908,6 +7941,10 @@ async function check(q, opts = {}) {
7908
7941
  offline,
7909
7942
  requestId: remote?.approvalRequestId
7910
7943
  });
7944
+ if (!org && telemetryEnabled()) {
7945
+ if (telemetryNoticePending()) process.stderr.write(c.muted("vet-skills sends anonymous usage (skill name, version, decision, agent type; never code, prompts or paths). Opt out: vet-skills telemetry off") + "\n");
7946
+ await sendTelemetry({ skill: resolved?.skill.name ?? q, semver: resolved?.version.semver ?? null, registry: resolved?.skill.registry ?? null, decision: decision.decision, would_be: decision.wouldBe ?? null, agent_kind: opts.agentKind ?? "other", hook_version: VERSION, os: platform(), offline }).catch(() => null);
7947
+ }
7911
7948
  return { resolved, decision, source, cacheAge, remote, offline, policyLabel };
7912
7949
  }
7913
7950
  function renderCheck(r) {
@@ -8153,10 +8190,11 @@ ${out}
8153
8190
  `);
8154
8191
  return 2;
8155
8192
  case "block":
8156
- process.stderr.write(`vet-skills blocked this install by org policy.
8193
+ process.stderr.write(`vet-skills blocked this install by ${readOrg() ? "org" : "your"} policy.
8157
8194
  ${renderCheck(r)}
8158
8195
  ${out}
8159
8196
  `);
8197
+ if (!readOrg() && hintOncePerDay("teams")) process.stderr.write("Running this for a team? One policy across every developer's agents, approvals and inventory: https://killerskills.ai/pricing\n");
8160
8198
  return 2;
8161
8199
  }
8162
8200
  return 0;
@@ -8211,6 +8249,11 @@ async function ask(question, choices) {
8211
8249
  }
8212
8250
  var USAGE = `vet-skills ${VERSION} \u2014 check agent skills and MCP servers before they run.
8213
8251
 
8252
+ Get started:
8253
+ vet-skills hook install claude-code check every install Claude Code makes (one time)
8254
+ vet-skills check <skill> see the verdict for anything before installing it
8255
+ vet-skills join <org> --token <t> enroll this machine in your team's policy (token from the dashboard)
8256
+
8214
8257
  vet-skills check <skill> dry-run the decision
8215
8258
  vet-skills install <skill> check, then \`npx skills add\`
8216
8259
  vet-skills detonate <path> [--publisher h] upload a local bundle to the sandbox and print its verdict
@@ -8396,7 +8439,7 @@ async function main() {
8396
8439
  machine_label: hostname(),
8397
8440
  agent_kind: values.agent ?? "other",
8398
8441
  hook_version: VERSION,
8399
- os: platform(),
8442
+ os: platform2(),
8400
8443
  installed: [],
8401
8444
  ci: values.ci === true
8402
8445
  });
@@ -8463,6 +8506,25 @@ async function main() {
8463
8506
  console.log(`${c.verified("\u2713")} ${handle} added to the personal block list${values.reason ? ` (${values.reason})` : ""}.`);
8464
8507
  return 0;
8465
8508
  }
8509
+ case "bug": {
8510
+ const last = readLog().at(-1);
8511
+ const qs = new URLSearchParams({ kind: "bug", source: "cli", cli_version: VERSION, ...last ? { skill: `${last.skill}@${last.semver}`, decision: last.decision } : {} });
8512
+ const url = `${API}/feedback?${qs}`;
8513
+ console.log(`Report a bug: ${url}`);
8514
+ const opener = platform2() === "darwin" ? "open" : platform2() === "win32" ? "start" : "xdg-open";
8515
+ spawnSync(opener, [url], { stdio: "ignore", shell: platform2() === "win32" });
8516
+ return 0;
8517
+ }
8518
+ case "telemetry": {
8519
+ const sub = rest[0] ?? "status";
8520
+ if (sub === "on" || sub === "off") {
8521
+ setTelemetry(sub === "on");
8522
+ console.log(`${c.verified("\u2713")} telemetry ${sub}`);
8523
+ return 0;
8524
+ }
8525
+ console.log(`telemetry ${telemetryEnabled() ? "on" : "off"} ${c.muted(readOrg() ? "(enrolled: decisions report to the org, not here)" : "(anonymous: skill, version, decision, agent type; never code, prompts or paths)")}`);
8526
+ return 0;
8527
+ }
8466
8528
  case "status": {
8467
8529
  const org = readOrg();
8468
8530
  const { policy, source } = readPolicy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vet-skills",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Vet agent skills and MCP servers before they run. Verdicts from KillerSkills, policy from your org.",
5
5
  "type": "module",
6
6
  "bin": {