skillfoxx 0.1.1 → 0.1.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.
package/README.en.md CHANGED
@@ -16,7 +16,7 @@ Requires Node.js 18.18 or newer.
16
16
  | `remove <entry>` | removes what SkillFoxx installed |
17
17
  | `update [entry]` | updates to the version SkillFoxx has rechecked |
18
18
  | `list` | shows what is installed |
19
- | `doctor` | checks installs: edited by hand, outdated, removed from the catalog |
19
+ | `doctor` | checks installs: edited by hand, outdated, removed from the catalog, environment variables not set |
20
20
  | `search <query>` | searches the catalog |
21
21
 
22
22
  Options: `--agent claude-code,cursor` (or `all`), `--project`, `--global`, `-y`/`--yes`, `--force`, `--dry-run`, `--json`, `--lang ru|en`.
@@ -41,6 +41,7 @@ Without `--agent` the CLI picks the agent it runs inside, otherwise every agent
41
41
  - A key SkillFoxx did not install, or one edited by hand, is not replaced without `--force`.
42
42
  - In a project a secret never goes into a file: the CLI writes an environment variable reference if the agent supports it, otherwise it prints instructions and suggests installing with `--global`.
43
43
  - For the user scope the secret value goes into the agent config, the way the agents do it themselves. Lock files hold no values.
44
+ - `doctor` checks required variables against the current environment (names and status only, no values; secrets are listed first among problems). On macOS an app opened from the Dock or Launchpad does not see exports from `~/.zshrc`: after `add`, such variables get a hint about `launchctl setenv` or starting the agent from a terminal where the variable is already set.
44
45
 
45
46
  ## Lock files
46
47
 
package/README.md CHANGED
@@ -16,7 +16,7 @@ npx skillfoxx add mcp/vv-mcp-server
16
16
  | `remove <запись>` | удаляет то, что поставил SkillFoxx |
17
17
  | `update [запись]` | обновляет до версии, которую сверил SkillFoxx |
18
18
  | `list` | показывает установленное |
19
- | `doctor` | проверяет: изменено руками, устарело, снято из каталога |
19
+ | `doctor` | проверяет: изменено руками, устарело, снято из каталога, не заданы переменные окружения |
20
20
  | `search <запрос>` | ищет в каталоге |
21
21
 
22
22
  Параметры: `--agent claude-code,cursor` (или `all`), `--project`, `--global`, `-y`/`--yes`, `--force`, `--dry-run`, `--json`, `--lang ru|en`.
@@ -41,6 +41,7 @@ Claude Code, Cursor, VS Code, Codex, Gemini CLI, Devin, Cline, Zoo Code, OpenCod
41
41
  - Ключ, который поставил не SkillFoxx или который изменили руками, без `--force` не заменяется.
42
42
  - В проекте секрет не пишется в файл никогда: пишется ссылка на переменную окружения, если агент ее понимает, иначе печатается инструкция и совет поставить с `--global`.
43
43
  - Для пользователя значение секрета пишется в конфиг агента, как это делают сами агенты. В lock-файле значений нет.
44
+ - `doctor` сверяет обязательные переменные записей с текущим окружением (только имена и статус, без значений; секреты в списке проблем идут первыми). На macOS приложение, открытое из Dock или Launchpad, не видит экспорт из `~/.zshrc`: после `add` для таких переменных печатается подсказка про `launchctl setenv` или запуск агента из терминала, где переменная уже задана.
44
45
 
45
46
  ## Lock-файлы
46
47
 
package/dist/cli.js CHANGED
@@ -20078,7 +20078,7 @@ var findOnPath = async (ctx, bin) => {
20078
20078
  };
20079
20079
 
20080
20080
  // src/version.ts
20081
- var VERSION = true ? "0.1.1" : "0.0.0-dev";
20081
+ var VERSION = true ? "0.1.2" : "0.0.0-dev";
20082
20082
 
20083
20083
  // src/api.ts
20084
20084
  var DAY = 24 * 36e5;
@@ -22465,6 +22465,33 @@ var describePlan = (ctx, actions, root) => actions.map((a) => {
22465
22465
  var planJson = (actions, root) => actions.map(
22466
22466
  (a) => a.type === "mcp" ? { type: a.type, agent: a.agent, component: a.component.name, file: path5.relative(root, a.target.file), key: a.target.key } : { type: a.type, agent: "agent" in a ? a.agent : null, component: componentName(a.component), ...a.type === "manual" ? { reason: a.reason.en } : {} }
22467
22467
  );
22468
+ var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
22469
+ var isLocalHost = (url2) => {
22470
+ try {
22471
+ const host = new URL(url2).hostname.toLowerCase();
22472
+ return LOCAL_HOSTS.has(host) || host.endsWith(".localhost");
22473
+ } catch {
22474
+ return false;
22475
+ }
22476
+ };
22477
+ var remoteAuthWarnings = (ctx, recipe) => {
22478
+ const warnings = [];
22479
+ for (const c of recipe.components) {
22480
+ if (c.kind !== "mcp-http" || isLocalHost(c.url)) continue;
22481
+ const authVars = new Set(c.headers.filter((h) => h.secret).flatMap((h) => templateVars(h.value)));
22482
+ const missing = [...authVars].filter((name2) => !ctx.env[name2]);
22483
+ if (missing.length) {
22484
+ warnings.push(
22485
+ tr(
22486
+ ctx,
22487
+ `${clean(c.name)}: \u0441\u0435\u0440\u0432\u0435\u0440 ${clean(c.url)} \u0431\u0443\u0434\u0435\u0442 \u0432\u044B\u0437\u0432\u0430\u043D \u0431\u0435\u0437 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043F\u043E\u043A\u0430 \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u0430 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F ${missing.join(", ")}.`,
22488
+ `${clean(c.name)}: the server ${clean(c.url)} will be called without authorization until the environment variable ${missing.join(", ")} is set.`
22489
+ )
22490
+ );
22491
+ }
22492
+ }
22493
+ return warnings;
22494
+ };
22468
22495
  var manualNote = (ctx, action, entryUrl) => {
22469
22496
  const c = action.component;
22470
22497
  const who = action.agent ? agentDef(action.agent).title : tr(ctx, "\u0412\u0440\u0443\u0447\u043D\u0443\u044E", "By hand");
@@ -22569,6 +22596,14 @@ var downloadSkill = async (ctx, source, loaded, component2) => {
22569
22596
  };
22570
22597
 
22571
22598
  // src/lock.ts
22599
+ var recipeVars = (recipe) => {
22600
+ const out = /* @__PURE__ */ new Map();
22601
+ for (const c of recipe.components) {
22602
+ if (c.kind !== "mcp-stdio" && c.kind !== "mcp-http") continue;
22603
+ for (const e of c.env) if (!out.has(e.name)) out.set(e.name, { name: e.name, required: e.required, secret: e.secret });
22604
+ }
22605
+ return [...out.values()];
22606
+ };
22572
22607
  var LOCK_FILE = "skillfoxx-lock.json";
22573
22608
  var exists2 = async (p) => Boolean(await stat4(p).catch(() => null));
22574
22609
  var upward = async (from, markers) => {
@@ -22605,6 +22640,7 @@ var lockPathOk = (scope, p) => {
22605
22640
  const abs = p.replace(/^[A-Za-z]:/, "");
22606
22641
  return abs.startsWith("/") && safeRelative(abs.slice(1));
22607
22642
  };
22643
+ var varOk = (v) => isObj(v) && isStr(v.name) && VAR_NAME.test(v.name) && typeof v.required === "boolean" && typeof v.secret === "boolean";
22608
22644
  var itemOk = (scope, i) => {
22609
22645
  if (!isObj(i)) return false;
22610
22646
  switch (i.kind) {
@@ -22625,7 +22661,7 @@ var itemOk = (scope, i) => {
22625
22661
  var entryOk = (scope, key, e) => {
22626
22662
  const m = ENTRY_KEY.exec(key);
22627
22663
  if (!m || !isObj(e)) return false;
22628
- return e.section === m[1] && e.slug === m[2] && e.scope === scope && isStr(e.recipeHash) && strOrNull(e.commitSha) && strOrNull(e.contentDigest) && isStr(e.status) && strOrNull(e.risk) && Array.isArray(e.agents) && e.agents.every(isAgent) && Array.isArray(e.items) && e.items.every((i) => itemOk(scope, i)) && isStr(e.installedAt) && isStr(e.updatedAt) && isStr(e.cliVersion);
22664
+ return e.section === m[1] && e.slug === m[2] && e.scope === scope && isStr(e.recipeHash) && strOrNull(e.commitSha) && strOrNull(e.contentDigest) && isStr(e.status) && strOrNull(e.risk) && Array.isArray(e.agents) && e.agents.every(isAgent) && Array.isArray(e.items) && e.items.every((i) => itemOk(scope, i)) && (e.vars === void 0 || Array.isArray(e.vars) && e.vars.every(varOk)) && isStr(e.installedAt) && isStr(e.updatedAt) && isStr(e.cliVersion);
22629
22665
  };
22630
22666
  var readLock = async (file3, scope) => {
22631
22667
  let text;
@@ -22979,7 +23015,7 @@ var knownSecrets = async (ctx, p) => {
22979
23015
  var install = async (ctx, p) => {
22980
23016
  const backup = new Backup(ctx);
22981
23017
  const created = [];
22982
- const res = { items: [], notes: [], touched: /* @__PURE__ */ new Set(), cliInstalled: false };
23018
+ const res = { items: [], notes: [], touched: /* @__PURE__ */ new Set(), cliInstalled: false, requiredEnvRefs: /* @__PURE__ */ new Set() };
22983
23019
  const prev = p.previous?.items ?? [];
22984
23020
  const owned = new Set(prev.flatMap((i) => i.kind === "skill" ? [lockItemTarget(ctx, p.scope, p.root, i)] : []).filter((x) => Boolean(x)));
22985
23021
  let rolledBack = null;
@@ -23087,6 +23123,7 @@ var installMcp = async (ctx, p, action, values, backup, prev, res) => {
23087
23123
  if (p.scope === "project" && action.agent === "codex") res.notes.push(tr(ctx, "Codex \u0447\u0438\u0442\u0430\u0435\u0442 .codex/config.toml \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0445 \u043F\u0440\u043E\u0435\u043A\u0442\u0430\u0445.", "Codex reads .codex/config.toml only in trusted projects."));
23088
23124
  if (p.scope === "project" && action.agent === "amp") res.notes.push(tr(ctx, "Amp: \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0441\u0435\u0440\u0432\u0435\u0440 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 amp mcp approve.", "Amp: approve the server with amp mcp approve."));
23089
23125
  if (r.references.length) res.notes.push(tr(ctx, `\u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043F\u0435\u0440\u0435\u0434 \u0437\u0430\u043F\u0443\u0441\u043A\u043E\u043C \u0430\u0433\u0435\u043D\u0442\u0430: ${r.references.join(", ")}`, `Set environment variables before starting the agent: ${r.references.join(", ")}`));
23126
+ for (const name2 of r.references) if (r.component.env.find((e) => e.name === name2)?.required) res.requiredEnvRefs.add(name2);
23090
23127
  if (r.secrets.length) res.notes.push(tr(ctx, `${title}: \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u0441\u0435\u043A\u0440\u0435\u0442\u043E\u0432 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u044B \u0432 ${file3}.`, `${title}: secret values are stored in ${file3}.`));
23091
23128
  };
23092
23129
  var installCli = async (ctx, p, action, res) => {
@@ -23167,6 +23204,21 @@ var findInstalled = async (ctx, input2, flags) => {
23167
23204
  };
23168
23205
 
23169
23206
  // src/commands/add.ts
23207
+ var envHint = (ctx, names) => {
23208
+ const list = names.join(", ");
23209
+ if (ctx.platform === "darwin") {
23210
+ return tr(
23211
+ ctx,
23212
+ `\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0443\u0436\u043D\u044B \u0430\u0433\u0435\u043D\u0442\u0443 \u043F\u0440\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0435: ${list}. \u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435, \u043E\u0442\u043A\u0440\u044B\u0442\u043E\u0435 \u0438\u0437 Dock \u0438\u043B\u0438 Launchpad, \u043D\u0435 \u0432\u0438\u0434\u0438\u0442 \u044D\u043A\u0441\u043F\u043E\u0440\u0442 \u0438\u0437 ~/.zshrc. \u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 launchctl setenv \u0418\u041C\u042F \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 (\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0434\u043E \u043F\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438) \u0438\u043B\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0439\u0442\u0435 \u0430\u0433\u0435\u043D\u0442\u0430 \u0438\u0437 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430, \u0433\u0434\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u0443\u0436\u0435 \u0437\u0430\u0434\u0430\u043D\u0430, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 open -a Cursor \u0438\u0437 \u044D\u0442\u043E\u0433\u043E \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430.`,
23213
+ `The agent needs these environment variables at launch: ${list}. An app opened from the Dock or Launchpad does not see exports from ~/.zshrc. Set the value with launchctl setenv NAME value (lasts until reboot), or start the agent from a terminal where the variable is already set, for example open -a Cursor from that terminal.`
23214
+ );
23215
+ }
23216
+ return tr(
23217
+ ctx,
23218
+ `\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0443\u0436\u043D\u044B \u0430\u0433\u0435\u043D\u0442\u0443 \u043F\u0440\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0435: ${list}. \u0417\u0430\u0434\u0430\u0439\u0442\u0435 \u0438\u0445 \u0432 \u043F\u0440\u043E\u0444\u0438\u043B\u0435 \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0438 \u0438\u043B\u0438 \u0432 \u0441\u043E\u0431\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0445 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u0430\u0433\u0435\u043D\u0442\u0430.`,
23219
+ `The agent needs these environment variables at launch: ${list}. Set them in your shell profile or in the agent's own environment settings.`
23220
+ );
23221
+ };
23170
23222
  var runAdd = async (ctx, args, flags) => {
23171
23223
  if (args.length !== 1) throw new CliError(64, tr(ctx, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043E\u0434\u043D\u0443 \u0437\u0430\u043F\u0438\u0441\u044C: npx skillfoxx add <\u0440\u0430\u0437\u0434\u0435\u043B>/<slug>", "Name one entry: npx skillfoxx add <section>/<slug>"));
23172
23224
  const { section, slug } = await resolveTarget(ctx, args[0]);
@@ -23176,7 +23228,7 @@ var runAdd = async (ctx, args, flags) => {
23176
23228
  if (!g.ok) throw new CliError(g.code, g.message);
23177
23229
  const b = body;
23178
23230
  const recipe = b.recipe;
23179
- for (const w of [...g.warnings, ...await checkRuntimes(ctx, recipe)]) ctx.err(w);
23231
+ for (const w of [...g.warnings, ...await checkRuntimes(ctx, recipe), ...remoteAuthWarnings(ctx, recipe)]) ctx.err(w);
23180
23232
  await riskConsent(ctx, g, Boolean(flags.yes));
23181
23233
  const scope = await pickScope(ctx, flags);
23182
23234
  const root = await rootFor(ctx, scope);
@@ -23207,12 +23259,15 @@ var runAdd = async (ctx, args, flags) => {
23207
23259
  scope,
23208
23260
  agents: [.../* @__PURE__ */ new Set([...previous?.agents ?? [], ...result.touched])],
23209
23261
  items: mergeItems(previous?.items ?? [], result.items),
23262
+ vars: recipeVars(recipe),
23210
23263
  installedAt: previous?.installedAt ?? now,
23211
23264
  updatedAt: now,
23212
23265
  cliVersion: VERSION
23213
23266
  };
23214
23267
  await writeLock(lockFile, lock);
23215
23268
  for (const note of new Set(result.notes)) ctx.out(note);
23269
+ const missingEnv = [...result.requiredEnvRefs].filter((n) => !ctx.env[n]);
23270
+ if (missingEnv.length) ctx.out(envHint(ctx, missingEnv));
23216
23271
  await sendEvents(ctx, installEvents(key, result, previous ? "update" : "add"));
23217
23272
  ctx.out(tr(ctx, `\u0413\u043E\u0442\u043E\u0432\u043E: ${key}`, `Done: ${key}`));
23218
23273
  return 0;
@@ -23220,6 +23275,8 @@ var runAdd = async (ctx, args, flags) => {
23220
23275
 
23221
23276
  // src/commands/doctor.ts
23222
23277
  import { lstat as lstat3, stat as stat5 } from "node:fs/promises";
23278
+ var envRows = (ctx, key, scope, vars) => (vars ?? []).map((v) => ({ key, scope, name: v.name, required: v.required, secret: v.secret, status: ctx.env[v.name] ? "ok" : "missing" }));
23279
+ var envProblems = (ctx, rows) => rows.filter((r) => r.required && r.status === "missing").sort((a, b) => Number(b.secret) - Number(a.secret)).map((r) => ({ key: r.key, scope: r.scope, kind: "env", detail: r.secret ? tr(ctx, `${r.name} (\u0441\u0435\u043A\u0440\u0435\u0442)`, `${r.name} (secret)`) : r.name }));
23223
23280
  var localProblems = async (ctx, key, scope, entry, root) => {
23224
23281
  const out = [];
23225
23282
  for (const item of entry.items) {
@@ -23259,15 +23316,20 @@ var LABEL = {
23259
23316
  blocked: ["\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u0430", "installation is blocked"],
23260
23317
  unsupported: ["\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F", "automatic install is no longer supported"],
23261
23318
  risk: ["\u0440\u0438\u0441\u043A \u0432\u044B\u0440\u043E\u0441 \u0434\u043E high", "risk went up to high"],
23262
- unchecked: ["\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C", "could not check"]
23319
+ unchecked: ["\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C", "could not check"],
23320
+ env: ["\u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u0430", "environment variable is not set"]
23263
23321
  };
23264
23322
  var runDoctor = async (ctx, _args, flags) => {
23265
23323
  const problems = [];
23324
+ const env = [];
23266
23325
  let checked = 0;
23267
23326
  for (const s of await lockScopes(ctx, flags)) {
23268
23327
  for (const [key, entry] of Object.entries(s.lock.entries)) {
23269
23328
  checked++;
23270
23329
  problems.push(...await localProblems(ctx, key, s.scope, entry, s.root));
23330
+ const rows = envRows(ctx, key, s.scope, entry.vars);
23331
+ env.push(...rows);
23332
+ problems.push(...envProblems(ctx, rows));
23271
23333
  try {
23272
23334
  const body = await fetchRecipe(ctx, entry.section, entry.slug);
23273
23335
  const g = gate(ctx, body);
@@ -23284,9 +23346,13 @@ var runDoctor = async (ctx, _args, flags) => {
23284
23346
  }
23285
23347
  }
23286
23348
  }
23287
- if (flags.json) printJson(ctx, { checked, problems });
23288
- else if (!problems.length) ctx.out(tr(ctx, `\u041F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E \u0437\u0430\u043F\u0438\u0441\u0435\u0439: ${checked}, \u043F\u0440\u043E\u0431\u043B\u0435\u043C \u043D\u0435\u0442.`, `Entries checked: ${checked}, no problems.`));
23289
- else for (const p of problems) ctx.out(`${p.key}: ${tr(ctx, ...LABEL[p.kind])}, ${p.detail}`);
23349
+ const optionalMissing = env.filter((r) => !r.required && r.status === "missing");
23350
+ if (flags.json) printJson(ctx, { checked, problems, env });
23351
+ else if (!problems.length && !optionalMissing.length) ctx.out(tr(ctx, `\u041F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E \u0437\u0430\u043F\u0438\u0441\u0435\u0439: ${checked}, \u043F\u0440\u043E\u0431\u043B\u0435\u043C \u043D\u0435\u0442.`, `Entries checked: ${checked}, no problems.`));
23352
+ else {
23353
+ for (const p of problems) ctx.out(`${p.key}: ${tr(ctx, ...LABEL[p.kind])}, ${p.detail}`);
23354
+ for (const r of optionalMissing) ctx.out(tr(ctx, `${r.key}: \u043D\u0435\u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u0430, ${r.name}`, `${r.key}: optional variable is not set, ${r.name}`));
23355
+ }
23290
23356
  return problems.length ? 2 : 0;
23291
23357
  };
23292
23358
 
@@ -23486,6 +23552,7 @@ var runUpdate = async (ctx, args, flags) => {
23486
23552
  risk: body.risk.level ?? null,
23487
23553
  agents: [...result.touched],
23488
23554
  items: result.items,
23555
+ vars: recipeVars(recipe),
23489
23556
  updatedAt: ctx.now().toISOString(),
23490
23557
  cliVersion: VERSION
23491
23558
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfoxx",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Установка скиллов, MCP-серверов и CLI из каталога SkillFoxx в Claude Code, Cursor, Codex и другие агенты",
5
5
  "type": "module",
6
6
  "bin": { "skillfoxx": "dist/cli.js" },