@rosthq/cli 0.7.142 → 0.7.143

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/dist/index.js CHANGED
@@ -53729,7 +53729,7 @@ External connectors are being rolled out provider by provider, conservatively (r
53729
53729
  order: 48,
53730
53730
  title: "CLI and MCP installation guide",
53731
53731
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
53732
- version: "2026-07-31.2",
53732
+ version: "2026-07-31.3",
53733
53733
  public: true,
53734
53734
  audiences: ["human", "cli", "mcp", "in_app_agent"],
53735
53735
  stages: ["company_setup", "staffing"],
@@ -54369,7 +54369,7 @@ Skills wrapper help:
54369
54369
  {{cli}} skills publish (--slug <slug>|--skill-version-id <id>) [--allow-warnings] [--json]
54370
54370
  \`\`\`
54371
54371
 
54372
- \`{{cli}} skills install\` writes into the client Skills directory and records \`.rost-skill.json\` with \`skill_version_id\`, \`content_sha256\`, source, client, and \`installed_from: "rost_api"\`. Set \`ROST_SKILL_INSTALL_ROOT\` to override the local root in CI or a sandbox. Use \`install\` for one Skill and \`sync\` as the default local-agent command because it installs every approved Skill assigned to the Seat and skips unchanged local copies by hash. Entitlement failures mean the tenant cannot access that {{brand}} catalog tier yet; stale local copies are fixed by rerunning \`sync\`; private catalog access is always served by {{brand}} APIs, not by giving the agent a private GitHub credential.
54372
+ \`{{cli}} skills install\` writes into the client Skills directory and records \`.rost-skill.json\` with \`skill_version_id\`, \`content_sha256\`, source, client, and \`installed_from: "rost_api"\`. Set \`ROST_SKILL_INSTALL_ROOT\` to override the local root in CI or a sandbox. Use \`install\` for one Skill and \`sync\` as the default local-agent command because it installs every approved Skill assigned to the Seat and skips a local copy only when its recorded hash matches and the installed file's own sha256 and byte size verify against the package the server returned; an edited or truncated local file is reinstalled instead of being reported unchanged. For \`--client claude-code\` the root is \`$CLAUDE_CONFIG_DIR/skills\` \u2014 \`CLAUDE_CONFIG_DIR\` is the variable Claude Code itself reads \u2014 otherwise \`~/.claude/skills\`; \`CLAUDE_HOME\` set on its own is a usage error with a migration instruction, and \`CLAUDE_HOME\` set alongside a different \`CLAUDE_CONFIG_DIR\` prints a notice that \`CLAUDE_HOME\` is ignored. For \`--client codex\` the root is \`$CODEX_HOME/skills\`, otherwise \`~/.codex/skills\`. For \`--client cursor\` the root is \`~/.cursor/skills\`. Catalog Skills land under \`<skills root>/rost/<slug>\`, except under \`ROST_SKILL_INSTALL_ROOT\`, where they land under \`<override root>/<client>/<slug>\`. A whitespace-only value for any of these variables is rejected rather than resolving somewhere unintended; an empty value means unset (the shell's \`FOO= cmd\` idiom for turning an inherited variable off) and falls back to the default root above. Entitlement failures mean the tenant cannot access that {{brand}} catalog tier yet; stale local copies are fixed by rerunning \`sync\`; private catalog access is always served by {{brand}} APIs, not by giving the agent a private GitHub credential.
54373
54373
 
54374
54374
  {{brand}}-maintained catalog entries are seeded from first-party Skill packages with stable content hashes, application descriptors, and declared required or optional tool dependencies. Catalog seed validation is fail-closed: a package with parser warnings is not published. Enabling a catalog Skill copies the current immutable version into the company library; later catalog refreshes update the global catalog row but do not silently rewrite already-enabled tenant Skill versions.
54375
54375
 
@@ -61954,7 +61954,6 @@ function ensureTrailingSlash2(value) {
61954
61954
 
61955
61955
  // src/skills.ts
61956
61956
  import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
61957
- import os2 from "node:os";
61958
61957
  import path5 from "node:path";
61959
61958
 
61960
61959
  // src/skill-install.ts
@@ -62473,7 +62472,7 @@ async function runInstallImplementation(context, args) {
62473
62472
  parsed = parseFlags2(args);
62474
62473
  client = parseClient2(optionalValue2(parsed, "client") ?? "codex");
62475
62474
  tag = optionalValue2(parsed, "tag") ?? DEFAULT_RELEASE_TAG;
62476
- installRoot(client, context.env);
62475
+ resolveSkillInstallRoot(client, context.env);
62477
62476
  } catch (error51) {
62478
62477
  if (error51 instanceof UsageError2) {
62479
62478
  writeErrorLine(context.io, error51.message);
@@ -62640,7 +62639,7 @@ async function installRelease(context, plan, testOverrides = {}) {
62640
62639
  let legacyNotice;
62641
62640
  let viaOverride;
62642
62641
  try {
62643
- ({ root, legacyNotice, viaOverride } = installRoot(plan.client, context.env));
62642
+ ({ root, legacyNotice, viaOverride } = resolveSkillInstallRoot(plan.client, context.env));
62644
62643
  } catch (error51) {
62645
62644
  if (error51 instanceof UsageError2) {
62646
62645
  writeErrorLine(context.io, error51.message);
@@ -62771,7 +62770,7 @@ function writeMarker(dir, input) {
62771
62770
  writeFileSync(path4.join(dir, SKILL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
62772
62771
  `, "utf8");
62773
62772
  }
62774
- function listInstalledFiles(dir) {
62773
+ function listInstalledFiles(dir, markerFile) {
62775
62774
  const files = [];
62776
62775
  const walk = (current, prefix) => {
62777
62776
  for (const name of readdirSync(current)) {
@@ -62785,7 +62784,7 @@ function listInstalledFiles(dir) {
62785
62784
  walk(absolute, relative);
62786
62785
  continue;
62787
62786
  }
62788
- if (relative === SKILL_MARKER_FILE) {
62787
+ if (relative === markerFile) {
62789
62788
  continue;
62790
62789
  }
62791
62790
  files.push({ path: relative, absolute, dev: stats.dev, ino: stats.ino });
@@ -62794,26 +62793,32 @@ function listInstalledFiles(dir) {
62794
62793
  walk(dir, "");
62795
62794
  return files;
62796
62795
  }
62797
- function verifyInstalledTree(dir, manifest) {
62796
+ function verifyInstalledTree(dir, manifest, options = {}) {
62797
+ const markerFile = options.markerFile ?? SKILL_MARKER_FILE;
62798
62798
  let present;
62799
62799
  try {
62800
- present = listInstalledFiles(dir);
62800
+ present = listInstalledFiles(dir, markerFile);
62801
62801
  } catch (error51) {
62802
62802
  return { ok: false, reason: error51 instanceof Error ? error51.message : "install directory is unreadable" };
62803
62803
  }
62804
- const manifestPaths = new Set(manifest.files.map((file2) => file2.path));
62805
- const unexpected = present.find((file2) => !manifestPaths.has(file2.path));
62804
+ const manifestByPath = new Map(manifest.files.map((file2) => [file2.path, file2]));
62805
+ const unexpected = present.find((file2) => !manifestByPath.has(file2.path));
62806
62806
  if (unexpected !== void 0) {
62807
- return { ok: false, reason: "an installed file is not listed in the signed manifest" };
62807
+ return { ok: false, reason: "an installed file is not listed in the package" };
62808
62808
  }
62809
- if (present.length !== manifestPaths.size) {
62810
- return { ok: false, reason: "the installed tree is missing files listed in the signed manifest" };
62809
+ if (present.length !== manifestByPath.size) {
62810
+ return { ok: false, reason: "the installed tree is missing files listed in the package" };
62811
62811
  }
62812
62812
  for (const file2 of present) {
62813
62813
  try {
62814
- if (statSync(file2.absolute).size > MAX_RESPONSE_BYTES) {
62814
+ const size = statSync(file2.absolute).size;
62815
+ if (size > MAX_RESPONSE_BYTES) {
62815
62816
  return { ok: false, reason: `an installed file exceeds the ${MAX_RESPONSE_BYTES}-byte verification cap: ${file2.path}` };
62816
62817
  }
62818
+ const declaredSize = manifestByPath.get(file2.path)?.sizeBytes;
62819
+ if (declaredSize !== void 0 && size !== declaredSize) {
62820
+ return { ok: false, reason: `an installed file's size does not match the declared size: ${file2.path}` };
62821
+ }
62817
62822
  } catch (error51) {
62818
62823
  return { ok: false, reason: error51 instanceof Error ? error51.message : "an installed file is unreadable" };
62819
62824
  }
@@ -62911,7 +62916,7 @@ function resolveCodexSkillsRoot(env) {
62911
62916
  const codexHome = requireNonBlank("CODEX_HOME", env.CODEX_HOME);
62912
62917
  return path4.resolve(path4.join(codexHome ?? path4.join(resolveHome(env), ".codex"), "skills"));
62913
62918
  }
62914
- function installRoot(client, env) {
62919
+ function resolveSkillInstallRoot(client, env) {
62915
62920
  const override = requireNonBlank("ROST_SKILL_INSTALL_ROOT", env.ROST_SKILL_INSTALL_ROOT);
62916
62921
  if (override !== null) {
62917
62922
  return { root: path4.resolve(override, client), legacyNotice: null, viaOverride: true };
@@ -62966,6 +62971,7 @@ function parseClient2(value) {
62966
62971
  }
62967
62972
 
62968
62973
  // src/skills.ts
62974
+ var CATALOG_SKILL_MARKER_FILE = ".rost-skill.json";
62969
62975
  var UsageError3 = class extends Error {
62970
62976
  };
62971
62977
  var CLIENTS2 = /* @__PURE__ */ new Set(["claude-code", "codex", "cursor"]);
@@ -63095,7 +63101,7 @@ async function installOne(context, args, env) {
63095
63101
  });
63096
63102
  const options = seatId ? { targetSeatId: seatId } : {};
63097
63103
  const output = await executeInstallCommand(context, body, options);
63098
- const summary = applyLocalSkillChanges(output, client, env, { removeRevoked: false });
63104
+ const summary = applyLocalSkillChanges(context.io, output, client, env, { removeRevoked: false });
63099
63105
  const outputRecord = asRecord4(output);
63100
63106
  context.io.stdout.write(parsed.json ? `${JSON.stringify({ ...outputRecord, local: summary }, null, 2)}
63101
63107
  ` : `${formatLocalSummary(summary)}
@@ -63110,7 +63116,7 @@ async function syncAssigned(context, args, env) {
63110
63116
  seat_id: seatId,
63111
63117
  client
63112
63118
  }, { targetSeatId: seatId });
63113
- const summary = applyLocalSkillChanges(output, client, env, { removeRevoked: true });
63119
+ const summary = applyLocalSkillChanges(context.io, output, client, env, { removeRevoked: true });
63114
63120
  const outputRecord = asRecord4(output);
63115
63121
  context.io.stdout.write(parsed.json ? `${JSON.stringify({ ...outputRecord, local: summary }, null, 2)}
63116
63122
  ` : `${formatLocalSummary(summary)}
@@ -63146,15 +63152,19 @@ async function executeSkillCommand(context, commandId, body, options = {}) {
63146
63152
  throw error51;
63147
63153
  }
63148
63154
  }
63149
- function applyLocalSkillChanges(output, client, env, options) {
63150
- const root = installRoot2(client, env);
63151
- const changes = writeInstalls(output, root);
63155
+ function applyLocalSkillChanges(io, output, client, env, options) {
63156
+ const { root, legacyNotice } = installRoot(client, env);
63157
+ if (legacyNotice !== null) {
63158
+ io.stderr.write(`${legacyNotice}
63159
+ `);
63160
+ }
63161
+ const changes = writeInstalls(io, output, root);
63152
63162
  if (options.removeRevoked) {
63153
63163
  changes.push(...removeRevokedInstalls(output, root));
63154
63164
  }
63155
63165
  return changes;
63156
63166
  }
63157
- function writeInstalls(output, root) {
63167
+ function writeInstalls(io, output, root) {
63158
63168
  const installs = asArray2(asRecord4(output).installs);
63159
63169
  return installs.map((entry) => {
63160
63170
  const install = asRecord4(entry);
@@ -63162,10 +63172,27 @@ function writeInstalls(output, root) {
63162
63172
  const metadata = asRecord4(install.metadata);
63163
63173
  const files = asArray2(install.files).map(asRecord4);
63164
63174
  const dir = path5.join(root, slug2);
63165
- const metadataPath = path5.join(dir, ".rost-skill.json");
63175
+ const metadataPath = path5.join(dir, CATALOG_SKILL_MARKER_FILE);
63166
63176
  const expectedHash = String(metadata.content_sha256 ?? "");
63167
63177
  if (existingHash(metadataPath) === expectedHash) {
63168
- return { slug: slug2, dir, status: "unchanged", files: files.length };
63178
+ const expected = files.map((file2) => {
63179
+ const declaredHash = file2.content_sha256;
63180
+ const sha256 = typeof declaredHash === "string" ? declaredHash : "";
63181
+ const sizeBytes = typeof file2.size_bytes === "number" ? file2.size_bytes : void 0;
63182
+ return {
63183
+ path: safeRelativePath2(String(file2.path ?? "")),
63184
+ sha256,
63185
+ ...sizeBytes === void 0 ? {} : { sizeBytes }
63186
+ };
63187
+ });
63188
+ const verified = verifyInstalledTree(dir, { files: expected }, { markerFile: CATALOG_SKILL_MARKER_FILE });
63189
+ if (verified.ok) {
63190
+ return { slug: slug2, dir, status: "unchanged", files: files.length };
63191
+ }
63192
+ io.stderr.write(
63193
+ `NOTICE: the installed Skill ${slug2} does not match the server package (${verified.reason}); reinstalling.
63194
+ `
63195
+ );
63169
63196
  }
63170
63197
  rmSync2(dir, { recursive: true, force: true });
63171
63198
  mkdirSync2(dir, { recursive: true });
@@ -63202,32 +63229,29 @@ function removeRevokedInstalls(output, root) {
63202
63229
  }
63203
63230
  return changes;
63204
63231
  }
63205
- function installRoot2(client, env) {
63206
- const override = env.ROST_SKILL_INSTALL_ROOT;
63207
- if (override && override.length > 0) {
63208
- return path5.resolve(override, client);
63209
- }
63210
- if (client === "codex") {
63211
- try {
63212
- return path5.join(resolveCodexSkillsRoot(env), "rost");
63213
- } catch (error51) {
63214
- if (error51 instanceof UsageError2) {
63215
- throw new UsageError3(error51.message);
63216
- }
63217
- throw error51;
63232
+ function installRoot(client, env) {
63233
+ let resolved;
63234
+ try {
63235
+ resolved = resolveSkillInstallRoot(client, env);
63236
+ } catch (error51) {
63237
+ if (error51 instanceof UsageError2) {
63238
+ throw new UsageError3(error51.message);
63218
63239
  }
63240
+ throw error51;
63219
63241
  }
63220
- if (client === "claude-code") {
63221
- return path5.join(env.CLAUDE_HOME ?? path5.join(os2.homedir(), ".claude"), "skills", "rost");
63222
- }
63223
- return path5.join(os2.homedir(), ".cursor", "skills", "rost");
63242
+ const root = resolved.viaOverride ? resolved.root : path5.join(resolved.root, "rost");
63243
+ return { root, legacyNotice: resolved.legacyNotice };
63224
63244
  }
63225
63245
  function safeRelativePath2(value) {
63226
63246
  const normalized = value.trim().replaceAll("\\", "/").replace(/^\.\/+/, "");
63227
63247
  if (!normalized || normalized.startsWith("/") || normalized.includes("//") || normalized.split("/").includes("..")) {
63228
63248
  throw new UsageError3("Server returned an invalid skill file path.");
63229
63249
  }
63230
- return normalized;
63250
+ const collapsed = normalized.split("/").filter((segment) => segment !== ".").join("/");
63251
+ if (!collapsed) {
63252
+ throw new UsageError3("Server returned an invalid skill file path.");
63253
+ }
63254
+ return collapsed;
63231
63255
  }
63232
63256
  function safeLocalSlug(value) {
63233
63257
  if (!/^[a-z0-9][a-z0-9-]{1,80}$/.test(value)) {
@@ -64661,7 +64685,7 @@ import { execFile as execFileCallback4 } from "node:child_process";
64661
64685
  import { createHash as createHash3, randomBytes } from "node:crypto";
64662
64686
  import { constants as fsConstants3 } from "node:fs";
64663
64687
  import { chmod, link, lstat, mkdir as mkdir5, mkdtemp, open, readdir as readdir3, readFile as readFile8, realpath, rename, rm as rm5, stat as stat4, writeFile as writeFile5 } from "node:fs/promises";
64664
- import os3 from "node:os";
64688
+ import os2 from "node:os";
64665
64689
  import path8 from "node:path";
64666
64690
  import { setTimeout as sleep2 } from "node:timers/promises";
64667
64691
  import { promisify as promisify4 } from "node:util";
@@ -64764,7 +64788,7 @@ function resolveSourceClaudeConfigDir(base) {
64764
64788
  return explicit;
64765
64789
  }
64766
64790
  const home = base.HOME;
64767
- const homeDir = typeof home === "string" && home.length > 0 ? home : os3.homedir();
64791
+ const homeDir = typeof home === "string" && home.length > 0 ? home : os2.homedir();
64768
64792
  return path8.join(homeDir, ".claude");
64769
64793
  }
64770
64794
  async function provisionIsolatedClaudeConfigDir(base, configDir, onRepair) {
@@ -64787,7 +64811,7 @@ function resolveSourceCodexHomeDir(base) {
64787
64811
  return explicit;
64788
64812
  }
64789
64813
  const home = base.HOME;
64790
- const homeDir = typeof home === "string" && home.length > 0 ? home : os3.homedir();
64814
+ const homeDir = typeof home === "string" && home.length > 0 ? home : os2.homedir();
64791
64815
  return path8.join(homeDir, ".codex");
64792
64816
  }
64793
64817
  var CODEX_HOME_TMP_PREFIX = ".rost-tmp-";