@zeroxyz/cli 1.0.0 → 1.2.0

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 +495 -86
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -76582,7 +76582,7 @@ init_esm_shims();
76582
76582
 
76583
76583
  // package.json
76584
76584
  var package_default = {
76585
- version: "1.0.0"};
76585
+ version: "1.2.0"};
76586
76586
 
76587
76587
  // src/app.ts
76588
76588
  init_esm_shims();
@@ -77578,7 +77578,7 @@ init_esm_shims();
77578
77578
  // ../sdk/dist/index.js
77579
77579
  init_esm_shims();
77580
77580
 
77581
- // ../sdk/dist/chunk-SE2M66VR.js
77581
+ // ../sdk/dist/chunk-SLXRHGE4.js
77582
77582
  init_esm_shims();
77583
77583
 
77584
77584
  // ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/index.js
@@ -144034,7 +144034,7 @@ var Payments = class {
144034
144034
  };
144035
144035
  };
144036
144036
  var package_default2 = {
144037
- version: "0.1.0"
144037
+ version: "0.2.0"
144038
144038
  };
144039
144039
  var SDK_VERSION2 = package_default2.version;
144040
144040
  var userWalletDtoSchema = external_exports3.object({
@@ -144999,7 +144999,7 @@ var Wallet = class {
144999
144999
  };
145000
145000
  };
145001
145001
  var DEFAULT_BASE_URL = "https://api.zero.xyz";
145002
- var DEFAULT_TIMEOUT_MS = 6e4;
145002
+ var DEFAULT_TIMEOUT_MS = 18e4;
145003
145003
  var DEFAULT_MAX_RETRIES = 2;
145004
145004
  var noopRefresh = async () => {
145005
145005
  };
@@ -145283,11 +145283,44 @@ var recordErrorFetchRun = async (client, opts, result) => {
145283
145283
  } catch {
145284
145284
  }
145285
145285
  };
145286
+ var recordTimeoutRun = async (client, opts, err, startedAt, capabilityIdRequested) => {
145287
+ const result = errorFetchResult(
145288
+ startedAt,
145289
+ err.message,
145290
+ "timeout",
145291
+ capabilityIdRequested,
145292
+ "network_error"
145293
+ );
145294
+ await recordErrorFetchRun(client, opts, result);
145295
+ };
145296
+ var sniffJsonShapeBytes = (buf) => {
145297
+ let i = 0;
145298
+ if (buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
145299
+ i = 3;
145300
+ }
145301
+ while (i < buf.length && (buf[i] === 32 || buf[i] === 9 || buf[i] === 10 || buf[i] === 13)) {
145302
+ i++;
145303
+ }
145304
+ if (i >= buf.length) return false;
145305
+ return buf[i] === 123 || buf[i] === 91;
145306
+ };
145307
+ var withDefaultContentType = (headers, body) => {
145308
+ if (!headers && body === void 0) return void 0;
145309
+ const next = headers ? { ...headers } : {};
145310
+ if (body === void 0) return next;
145311
+ const hasContentType = Object.keys(next).some(
145312
+ (k3) => k3.toLowerCase() === "content-type"
145313
+ );
145314
+ if (!hasContentType) {
145315
+ next["content-type"] = typeof body === "string" || sniffJsonShapeBytes(body) ? "application/json" : "application/octet-stream";
145316
+ }
145317
+ return next;
145318
+ };
145286
145319
  var clientFetch = async (client, url4, opts = {}) => {
145287
145320
  const startedAt = Date.now();
145288
145321
  const capabilityIdRequested = opts.capabilityId !== void 0;
145289
145322
  const method = opts.method ?? (opts.body !== void 0 ? "POST" : "GET");
145290
- const probeHeaders = opts.headers ? { ...opts.headers } : void 0;
145323
+ const probeHeaders = withDefaultContentType(opts.headers, opts.body);
145291
145324
  const requestInit = {
145292
145325
  method,
145293
145326
  headers: probeHeaders,
@@ -145304,6 +145337,16 @@ var clientFetch = async (client, url4, opts = {}) => {
145304
145337
  try {
145305
145338
  response = await configuredFetch(url4, requestInit);
145306
145339
  } catch (err) {
145340
+ if (err instanceof ZeroTimeoutError) {
145341
+ await recordTimeoutRun(
145342
+ client,
145343
+ opts,
145344
+ err,
145345
+ startedAt,
145346
+ capabilityIdRequested
145347
+ );
145348
+ throw err;
145349
+ }
145307
145350
  if (err instanceof ZeroError) throw err;
145308
145351
  const errMessage = err instanceof Error ? err.message : String(err);
145309
145352
  const result2 = errorFetchResult(
@@ -145325,8 +145368,9 @@ var clientFetch = async (client, url4, opts = {}) => {
145325
145368
  url: url4,
145326
145369
  method,
145327
145370
  // Fresh clone for the paid retry — same rationale as
145328
- // the probe-side clone above.
145329
- headers: opts.headers ? { ...opts.headers } : void 0,
145371
+ // the probe-side clone above. Re-derives the default
145372
+ // Content-Type so the paid leg matches the probe.
145373
+ headers: withDefaultContentType(opts.headers, opts.body),
145330
145374
  body: opts.body,
145331
145375
  maxPay: opts.maxPay,
145332
145376
  account: opts.account,
@@ -145362,6 +145406,15 @@ var clientFetch = async (client, url4, opts = {}) => {
145362
145406
  );
145363
145407
  await recordErrorFetchRun(client, opts, result2);
145364
145408
  return result2;
145409
+ } else if (err instanceof ZeroTimeoutError) {
145410
+ await recordTimeoutRun(
145411
+ client,
145412
+ opts,
145413
+ err,
145414
+ startedAt,
145415
+ capabilityIdRequested
145416
+ );
145417
+ throw err;
145365
145418
  } else {
145366
145419
  throw err;
145367
145420
  }
@@ -146367,7 +146420,7 @@ var fetchCommand = (appContext) => new Command("fetch").description(
146367
146420
  "Read the request body from stdin (equivalent to `-d @-`)."
146368
146421
  ).option("-H, --header <header...>", "Headers in Key:Value format").option("--max-pay <amount>", "Maximum per-call spend limit (USDC)").option(
146369
146422
  "--timeout <seconds>",
146370
- "Per-request timeout in seconds, overriding the default (60). Applies to each HTTP leg of the call (probe and paid retry), not as a wall-clock deadline."
146423
+ "Per-request timeout in seconds, overriding the default (180). Applies to each HTTP leg of the call (probe and paid retry), not as a wall-clock deadline."
146371
146424
  ).option(
146372
146425
  "--capability <id>",
146373
146426
  "Bind this fetch to a capability (uid or slug) so a reviewable run is recorded even without a prior `zero search`"
@@ -146858,29 +146911,140 @@ var formatRating = (rating) => {
146858
146911
  return `${successPct} success, ${reviews} reviews`;
146859
146912
  };
146860
146913
  var asNode = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
146861
- var placeholderFor = (fieldName, propSchema) => {
146914
+ var schemaType = (node) => {
146915
+ const t = node?.type;
146916
+ if (typeof t === "string") return t;
146917
+ if (Array.isArray(t)) {
146918
+ const first = t.find((x2) => typeof x2 === "string" && x2 !== "null");
146919
+ return typeof first === "string" ? first : void 0;
146920
+ }
146921
+ return void 0;
146922
+ };
146923
+ var coerceToType = (value, type2) => {
146924
+ if (typeof value !== "string") return value;
146925
+ if (type2 === "number" || type2 === "integer") {
146926
+ const n = Number(value);
146927
+ return value.trim() !== "" && Number.isFinite(n) ? n : value;
146928
+ }
146929
+ if (type2 === "boolean") {
146930
+ if (value === "true") return true;
146931
+ if (value === "false") return false;
146932
+ }
146933
+ return value;
146934
+ };
146935
+ var sampleValueFor = (fieldName, propSchema) => {
146862
146936
  const node = asNode(propSchema);
146937
+ const type2 = schemaType(node);
146863
146938
  const example = node?.example ?? node?.default;
146864
- if (typeof example === "string") return example;
146865
- if (typeof example === "number" || typeof example === "boolean") {
146866
- return String(example);
146939
+ if (example !== void 0 && example !== null) {
146940
+ return coerceToType(example, type2);
146941
+ }
146942
+ switch (type2) {
146943
+ case "number":
146944
+ case "integer":
146945
+ return 0;
146946
+ case "boolean":
146947
+ return false;
146948
+ case "array":
146949
+ return [];
146950
+ case "object":
146951
+ return {};
146952
+ default:
146953
+ return `<${fieldName.toUpperCase()}>`;
146867
146954
  }
146868
- return `<${fieldName.toUpperCase()}>`;
146869
146955
  };
146956
+ var placeholderFor = (fieldName, propSchema) => String(sampleValueFor(fieldName, propSchema));
146870
146957
  var extractInputEnvelope = (bodySchema, method) => {
146871
- if (!bodySchema) return {};
146872
- const props = asNode(bodySchema.properties);
146958
+ const root = asNode(bodySchema);
146959
+ if (!root) return {};
146960
+ const props = asNode(root.properties);
146873
146961
  if (!props) return {};
146874
146962
  const inputProps = asNode(asNode(props.input)?.properties);
146875
146963
  if (inputProps) {
146876
146964
  return {
146877
- queryParams: asNode(asNode(inputProps.queryParams)?.properties) ?? void 0,
146878
- body: asNode(asNode(inputProps.body)?.properties) ?? void 0
146965
+ queryParams: asNode(inputProps.queryParams) ?? void 0,
146966
+ body: asNode(inputProps.body) ?? void 0
146879
146967
  };
146880
146968
  }
146881
146969
  const upperMethod = method.toUpperCase();
146882
146970
  const isGetLike = upperMethod === "GET" || upperMethod === "DELETE";
146883
- return isGetLike ? { queryParams: props } : { body: props };
146971
+ return isGetLike ? { queryParams: root } : { body: root };
146972
+ };
146973
+ var schemaTypeLabel = (node) => {
146974
+ const t = node.type;
146975
+ if (typeof t === "string") return t;
146976
+ if (Array.isArray(t)) {
146977
+ const parts = t.filter((x2) => typeof x2 === "string");
146978
+ return parts.length > 0 ? parts.join(" | ") : "any";
146979
+ }
146980
+ return "any";
146981
+ };
146982
+ var constraintHint = (node) => {
146983
+ const num2 = (k3) => typeof node[k3] === "number" ? node[k3] : void 0;
146984
+ const min = num2("minimum");
146985
+ const max = num2("maximum");
146986
+ if (min !== void 0 && max !== void 0) return `range: ${min}..${max}`;
146987
+ if (min !== void 0) return `min: ${min}`;
146988
+ if (max !== void 0) return `max: ${max}`;
146989
+ const minLen = num2("minLength");
146990
+ const maxLen = num2("maxLength");
146991
+ if (minLen !== void 0 && maxLen !== void 0)
146992
+ return `length: ${minLen}..${maxLen}`;
146993
+ if (maxLen !== void 0) return `maxLength: ${maxLen}`;
146994
+ if (minLen !== void 0) return `minLength: ${minLen}`;
146995
+ return null;
146996
+ };
146997
+ var renderParam = (name, propSchema, required3) => {
146998
+ const node = asNode(propSchema) ?? {};
146999
+ const attrs = [schemaTypeLabel(node)];
147000
+ if (required3) attrs.push("required");
147001
+ if (node.default !== void 0)
147002
+ attrs.push(`default: ${JSON.stringify(node.default)}`);
147003
+ if (Array.isArray(node.enum))
147004
+ attrs.push(`one of: ${node.enum.map((v) => String(v)).join(" | ")}`);
147005
+ const hint = constraintHint(node);
147006
+ if (hint) attrs.push(hint);
147007
+ const lines = [` ${name} (${attrs.join(", ")})`];
147008
+ const desc = typeof node.description === "string" ? node.description.trim() : "";
147009
+ if (desc) lines.push(` ${desc}`);
147010
+ return lines;
147011
+ };
147012
+ var renderProps = (node) => {
147013
+ const props = node ? asNode(node.properties) : null;
147014
+ if (!props) return [];
147015
+ const entries = Object.entries(props);
147016
+ if (entries.length === 0) return [];
147017
+ const requiredSet = new Set(
147018
+ Array.isArray(node?.required) ? node.required.filter((x2) => typeof x2 === "string") : []
147019
+ );
147020
+ return entries.flatMap(
147021
+ ([name, schema]) => renderParam(name, schema, requiredSet.has(name))
147022
+ );
147023
+ };
147024
+ var buildSchemaSection = (capability) => {
147025
+ const { queryParams, body } = extractInputEnvelope(
147026
+ capability.bodySchema,
147027
+ capability.method
147028
+ );
147029
+ return [...renderProps(queryParams), ...renderProps(body)];
147030
+ };
147031
+ var buildResponseSection = (capability) => {
147032
+ const rs = asNode(capability.responseSchema);
147033
+ if (!rs) return [];
147034
+ if (rs.example !== void 0) {
147035
+ const example = JSON.stringify(rs.example);
147036
+ const truncated = example.length > 300 ? `${example.slice(0, 300)}\u2026` : example;
147037
+ return [" Returns (example):", ` ${truncated}`];
147038
+ }
147039
+ const props = asNode(rs.properties);
147040
+ if (props) {
147041
+ const fields = Object.entries(props).map(
147042
+ ([k3, v]) => `${k3}: ${schemaTypeLabel(asNode(v) ?? {})}`
147043
+ );
147044
+ if (fields.length > 0)
147045
+ return [" Returns:", ` { ${fields.join(", ")} }`];
147046
+ }
147047
+ return [];
146884
147048
  };
146885
147049
  var buildTryItExample = (capability) => {
146886
147050
  const method = capability.method.toUpperCase();
@@ -146894,7 +147058,8 @@ var buildTryItExample = (capability) => {
146894
147058
  ([k3, v]) => `-H "${k3}: ${v}" # [caller-provided]`
146895
147059
  );
146896
147060
  if (method === "GET" || queryParams && !body) {
146897
- const qs = queryParams ? Object.entries(queryParams).map(
147061
+ const qpProps = queryParams ? asNode(queryParams.properties) : null;
147062
+ const qs = qpProps ? Object.entries(qpProps).map(
146898
147063
  ([k3, schema]) => `${encodeURIComponent(k3)}=${encodeURIComponent(placeholderFor(k3, schema))}`
146899
147064
  ).join("&") : "";
146900
147065
  const url4 = qs ? `${capability.url}?${qs}` : capability.url;
@@ -146908,15 +147073,16 @@ var buildTryItExample = (capability) => {
146908
147073
  }
146909
147074
  if (!queryParams && method === "GET") {
146910
147075
  lines.push(
146911
- " # bodySchema did not expose queryParams \u2014 call the URL as-is or inspect the raw schema above."
147076
+ " # bodySchema did not expose queryParams \u2014 call the URL as-is."
146912
147077
  );
146913
147078
  }
146914
147079
  return lines;
146915
147080
  }
146916
- const samplePayload = body ? Object.fromEntries(
146917
- Object.entries(body).map(([k3, schema]) => [
147081
+ const bodyProps = body ? asNode(body.properties) : null;
147082
+ const samplePayload = bodyProps ? Object.fromEntries(
147083
+ Object.entries(bodyProps).map(([k3, schema]) => [
146918
147084
  k3,
146919
- placeholderFor(k3, schema)
147085
+ sampleValueFor(k3, schema)
146920
147086
  ])
146921
147087
  ) : null;
146922
147088
  const bodyJson = samplePayload ? JSON.stringify(samplePayload) : "<BODY_JSON>";
@@ -146924,9 +147090,9 @@ var buildTryItExample = (capability) => {
146924
147090
  if (method !== "POST") lines.push(` -X ${method} \\`);
146925
147091
  for (const h2 of headerFlags) lines.push(` ${h2} \\`);
146926
147092
  lines.push(` -d '${bodyJson}'`);
146927
- if (!body) {
147093
+ if (!bodyProps) {
146928
147094
  lines.push(
146929
- " # bodySchema did not expose input.body \u2014 replace <BODY_JSON> with the exact shape shown above."
147095
+ " # bodySchema did not expose input.body \u2014 replace <BODY_JSON> with the documented request shape."
146930
147096
  );
146931
147097
  }
146932
147098
  return lines;
@@ -146948,6 +147114,12 @@ var formatCapability = (capability) => {
146948
147114
  lines.push(` ${promptLine}`);
146949
147115
  }
146950
147116
  }
147117
+ const schemaLines = buildSchemaSection(capability);
147118
+ if (schemaLines.length > 0) {
147119
+ lines.push("", "Parameters:");
147120
+ lines.push(...schemaLines);
147121
+ }
147122
+ lines.push(...buildResponseSection(capability));
146951
147123
  lines.push(...buildTryItExample(capability));
146952
147124
  return lines.join("\n");
146953
147125
  };
@@ -147020,9 +147192,293 @@ var getCommand = (appContext) => new Command("get").description(
147020
147192
 
147021
147193
  // src/commands/init-command.ts
147022
147194
  init_esm_shims();
147023
- var INIT_DEPRECATION_MESSAGE = "The recommended way to install Zero and initialize its skill and hooks is via Agent Plugins. See the Zero agent plugin at github.com/officialzeroxyz/zero-plugins. If you need to install Zero manually you can find the binary at https://releases.zero.xyz/latest/zero-<os>-<arch> (one of: zero-macos-arm64, zero-macos-x64, zero-linux-x64, zero-linux-arm64, zero-win-x64.exe) or use npm to install it globally `npm i -g @zeroxyz/cli@latest`. You can find the Zero skill at https://www.zero.xyz/SKILL.md where it can be manually configured with your agent.";
147024
- var initCommand = (_appContext) => new Command("init").description("Deprecated \u2014 install Zero via Agent Plugins").allowExcessArguments(true).allowUnknownOption(true).action(() => {
147025
- console.log(INIT_DEPRECATION_MESSAGE);
147195
+
147196
+ // src/util/install-banner.ts
147197
+ init_esm_shims();
147198
+ var colorsEnabled = () => {
147199
+ if (process.env.NO_COLOR) return false;
147200
+ if (process.env.FORCE_COLOR) return true;
147201
+ return Boolean(process.stdout.isTTY);
147202
+ };
147203
+ var wrap5 = (code, text) => colorsEnabled() ? `\x1B[${code}m${text}\x1B[0m` : text;
147204
+ var color = {
147205
+ bold: (s) => wrap5("1", s),
147206
+ dim: (s) => wrap5("2", s),
147207
+ cyan: (s) => wrap5("36", s),
147208
+ magenta: (s) => wrap5("35", s),
147209
+ green: (s) => wrap5("32", s),
147210
+ yellow: (s) => wrap5("33", s),
147211
+ red: (s) => wrap5("31", s),
147212
+ gray: (s) => wrap5("90", s),
147213
+ boldCyan: (s) => wrap5("1;36", s),
147214
+ boldMagenta: (s) => wrap5("1;35", s),
147215
+ boldGreen: (s) => wrap5("1;32", s),
147216
+ boldRed: (s) => wrap5("1;31", s)
147217
+ };
147218
+ var supportsUnicode = () => {
147219
+ if (process.platform !== "win32") return true;
147220
+ return Boolean(
147221
+ process.env.WT_SESSION || process.env.TERM_PROGRAM === "vscode"
147222
+ );
147223
+ };
147224
+ var SYMBOLS = supportsUnicode() ? { check: "\u2713", arrow: "\u203A", warn: "!" } : { check: "OK", arrow: ">", warn: "!" };
147225
+ var stepSuccess = (label, detail) => {
147226
+ const line = detail ? ` ${color.boldGreen(SYMBOLS.check)} ${label} ${color.dim(detail)}` : ` ${color.boldGreen(SYMBOLS.check)} ${label}`;
147227
+ console.log(line);
147228
+ };
147229
+ var stepWarn = (label, detail) => {
147230
+ const line = detail ? ` ${color.yellow(SYMBOLS.warn)} ${label} ${color.dim(detail)}` : ` ${color.yellow(SYMBOLS.warn)} ${label}`;
147231
+ console.log(line);
147232
+ };
147233
+ var stepSkip = (label, detail) => {
147234
+ const line = detail ? ` ${color.gray(SYMBOLS.arrow)} ${color.dim(`${label} ${detail}`)}` : ` ${color.gray(SYMBOLS.arrow)} ${color.dim(label)}`;
147235
+ console.log(line);
147236
+ };
147237
+ var stepInfo = (message2) => {
147238
+ console.log(` ${color.gray("\xB7")} ${color.dim(message2)}`);
147239
+ };
147240
+ var sectionDivider = () => {
147241
+ console.log("");
147242
+ console.log(color.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
147243
+ console.log("");
147244
+ };
147245
+
147246
+ // src/commands/init-command.ts
147247
+ var SKILL_DIRS = [".claude/skills", ".agents/skills"];
147248
+ var ZERO_SANDBOX_DOMAIN = "*.zero.xyz";
147249
+ var PLUGIN_HOOKS_ROOT = "${CLAUDE_PLUGIN_ROOT}/hooks";
147250
+ var SCRIPT_NAME_PATTERN = /\$\{CLAUDE_PLUGIN_ROOT\}\/hooks\/([a-z0-9][a-z0-9-]*\.sh)/;
147251
+ var fetchText = async (url4) => {
147252
+ const res = await fetch(url4);
147253
+ if (!res.ok) {
147254
+ throw new Error(`GET ${url4} failed with ${res.status}`);
147255
+ }
147256
+ return await res.text();
147257
+ };
147258
+ var entryScriptNames = (entry) => {
147259
+ const names = [];
147260
+ for (const hook of entry.hooks ?? []) {
147261
+ if (typeof hook.command !== "string") continue;
147262
+ const match = hook.command.match(SCRIPT_NAME_PATTERN);
147263
+ if (match?.[1]) names.push(match[1]);
147264
+ }
147265
+ return names;
147266
+ };
147267
+ var expandHome = (path3, home) => {
147268
+ if (path3 === "~") return home;
147269
+ if (path3.startsWith("~/")) return join(home, path3.slice(2));
147270
+ return path3;
147271
+ };
147272
+ var installSkill = (skillsDirs, skillMd, verbose) => {
147273
+ const installed = [];
147274
+ for (const dir of skillsDirs) {
147275
+ const dest = join(dir, "zero");
147276
+ try {
147277
+ mkdirSync(dest, { recursive: true });
147278
+ writeFileSync(join(dest, "SKILL.md"), skillMd);
147279
+ installed.push(join(dest, "SKILL.md"));
147280
+ if (verbose) stepInfo(`wrote ${join(dest, "SKILL.md")}`);
147281
+ } catch (err) {
147282
+ stepWarn(
147283
+ `Could not write the skill under ${dir}`,
147284
+ err instanceof Error ? err.message : String(err)
147285
+ );
147286
+ }
147287
+ }
147288
+ return installed;
147289
+ };
147290
+ var installHookScripts = async (home, hooksJson, webBase, verbose) => {
147291
+ const names = /* @__PURE__ */ new Set();
147292
+ for (const entries of Object.values(hooksJson.hooks)) {
147293
+ for (const entry of entries ?? []) {
147294
+ for (const name of entryScriptNames(entry)) names.add(name);
147295
+ }
147296
+ }
147297
+ if (names.size === 0) {
147298
+ throw new Error("hooks.json declared no recognizable hook scripts");
147299
+ }
147300
+ const hooksDir = join(home, ".zero", "hooks");
147301
+ mkdirSync(hooksDir, { recursive: true });
147302
+ return await Promise.all(
147303
+ [...names].map(async (name) => {
147304
+ const body = await fetchText(`${webBase}/hooks/${name}`);
147305
+ const dest = join(hooksDir, name);
147306
+ writeFileSync(dest, body);
147307
+ chmodSync(dest, 493);
147308
+ if (verbose) stepInfo(`wrote ${dest}`);
147309
+ return name;
147310
+ })
147311
+ );
147312
+ };
147313
+ var registerHooksInClaudeSettings = (home, hooksJson, verbose) => {
147314
+ const hooksDir = join(home, ".zero", "hooks");
147315
+ const settingsPath = join(home, ".claude", "settings.json");
147316
+ let settings = {};
147317
+ if (existsSync(settingsPath)) {
147318
+ try {
147319
+ settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
147320
+ } catch {
147321
+ stepWarn(
147322
+ "Hooks not registered",
147323
+ `${settingsPath} is not valid JSON \u2014 fix it and re-run \`zero init\``
147324
+ );
147325
+ return false;
147326
+ }
147327
+ }
147328
+ if (!settings.hooks || typeof settings.hooks !== "object") {
147329
+ settings.hooks = {};
147330
+ }
147331
+ const hooks = settings.hooks;
147332
+ for (const [event, entries] of Object.entries(hooksJson.hooks)) {
147333
+ for (const entry of entries ?? []) {
147334
+ const scripts = entryScriptNames(entry);
147335
+ if (scripts.length === 0) {
147336
+ if (verbose) {
147337
+ stepInfo(`${event}: entry has no plugin hook script \u2014 skipped`);
147338
+ }
147339
+ continue;
147340
+ }
147341
+ const rewritten = {
147342
+ ...entry,
147343
+ hooks: (entry.hooks ?? []).map((hook) => ({
147344
+ ...hook,
147345
+ command: typeof hook.command === "string" ? hook.command.replaceAll(PLUGIN_HOOKS_ROOT, hooksDir) : hook.command
147346
+ }))
147347
+ };
147348
+ if (!Array.isArray(hooks[event])) hooks[event] = [];
147349
+ const list2 = hooks[event];
147350
+ const existingIdx = list2.findIndex((existing) => {
147351
+ const existingHooks = existing?.hooks;
147352
+ if (!Array.isArray(existingHooks)) return false;
147353
+ return existingHooks.some(
147354
+ (h2) => typeof h2.command === "string" && scripts.some((s) => h2.command.includes(s))
147355
+ );
147356
+ });
147357
+ if (existingIdx >= 0) {
147358
+ list2[existingIdx] = rewritten;
147359
+ if (verbose) stepInfo(`${event}: ${scripts.join(", ")} entry replaced`);
147360
+ } else {
147361
+ list2.push(rewritten);
147362
+ if (verbose) stepInfo(`${event}: ${scripts.join(", ")} entry appended`);
147363
+ }
147364
+ }
147365
+ }
147366
+ if (!settings.sandbox || typeof settings.sandbox !== "object") {
147367
+ settings.sandbox = {};
147368
+ }
147369
+ const sandbox = settings.sandbox;
147370
+ if (!sandbox.network || typeof sandbox.network !== "object") {
147371
+ sandbox.network = {};
147372
+ }
147373
+ const network = sandbox.network;
147374
+ if (!Array.isArray(network.allowedDomains)) {
147375
+ network.allowedDomains = [];
147376
+ }
147377
+ const allowedDomains = network.allowedDomains;
147378
+ if (!allowedDomains.includes(ZERO_SANDBOX_DOMAIN)) {
147379
+ allowedDomains.push(ZERO_SANDBOX_DOMAIN);
147380
+ if (verbose) {
147381
+ stepInfo(`sandbox.network.allowedDomains += ${ZERO_SANDBOX_DOMAIN}`);
147382
+ }
147383
+ }
147384
+ mkdirSync(join(home, ".claude"), { recursive: true });
147385
+ writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
147386
+ `);
147387
+ return true;
147388
+ };
147389
+ var runInit = async (appContext, options = {}) => {
147390
+ const verbose = options.verbose ?? false;
147391
+ appContext.services.analyticsService.capture("init_started", {});
147392
+ let currentStep = "fetch";
147393
+ try {
147394
+ console.log("");
147395
+ console.log(` ${color.boldCyan("Installing Zero")}`);
147396
+ console.log("");
147397
+ console.log(color.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
147398
+ console.log("");
147399
+ const home = homedir();
147400
+ const webBase = appContext.env.ZERO_WEB_URL;
147401
+ const [skillMd, hooksJsonRaw] = await Promise.all([
147402
+ fetchText(`${webBase}/SKILL.md`),
147403
+ fetchText(`${webBase}/hooks.json`)
147404
+ ]);
147405
+ let hooksJson;
147406
+ try {
147407
+ hooksJson = JSON.parse(hooksJsonRaw);
147408
+ } catch {
147409
+ throw new Error(`${webBase}/hooks.json is not valid JSON`);
147410
+ }
147411
+ if (!hooksJson?.hooks || typeof hooksJson.hooks !== "object") {
147412
+ throw new Error(`${webBase}/hooks.json has no "hooks" object`);
147413
+ }
147414
+ if (verbose) {
147415
+ stepInfo(`fetched ${webBase}/SKILL.md and ${webBase}/hooks.json`);
147416
+ }
147417
+ currentStep = "skill";
147418
+ const skillsDirs = [
147419
+ .../* @__PURE__ */ new Set([
147420
+ ...SKILL_DIRS.map((dir) => join(home, dir)),
147421
+ ...(options.skillsDir ?? []).map((dir) => expandHome(dir, home))
147422
+ ])
147423
+ ];
147424
+ const skillsInstalled = installSkill(skillsDirs, skillMd, verbose);
147425
+ if (skillsInstalled.length === 0) {
147426
+ throw new Error("could not write the Zero skill to any skills directory");
147427
+ }
147428
+ stepSuccess("Skill installed", `${skillsInstalled.length} locations`);
147429
+ currentStep = "hooks";
147430
+ const hookScriptsInstalled = await installHookScripts(
147431
+ home,
147432
+ hooksJson,
147433
+ webBase,
147434
+ verbose
147435
+ );
147436
+ stepSuccess("Hook scripts installed", "~/.zero/hooks");
147437
+ currentStep = "settings";
147438
+ const settingsRegistered = registerHooksInClaudeSettings(
147439
+ home,
147440
+ hooksJson,
147441
+ verbose
147442
+ );
147443
+ if (settingsRegistered) {
147444
+ stepSuccess("Hooks registered", "~/.claude/settings.json");
147445
+ }
147446
+ currentStep = "complete";
147447
+ appContext.services.analyticsService.capture("init_completed", {
147448
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
147449
+ skills_installed: skillsInstalled,
147450
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
147451
+ skills_installed_count: skillsInstalled.length,
147452
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
147453
+ hook_scripts_installed: hookScriptsInstalled,
147454
+ // biome-ignore lint/style/useNamingConvention: snake_case for analytics
147455
+ settings_registered: settingsRegistered
147456
+ });
147457
+ sectionDivider();
147458
+ console.log(` ${color.boldGreen("Zero is set up.")}`);
147459
+ console.log(
147460
+ ` ${color.dim("Next: sign in with `zero auth login` \u2014 it prints a URL to approve in your browser.")}`
147461
+ );
147462
+ console.log("");
147463
+ return { skillsInstalled, hookScriptsInstalled, settingsRegistered };
147464
+ } catch (err) {
147465
+ appContext.services.analyticsService.capture("init_failed", {
147466
+ step: currentStep,
147467
+ error: truncateError(err instanceof Error ? err.message : String(err))
147468
+ });
147469
+ throw err;
147470
+ }
147471
+ };
147472
+ var initCommand = (appContext) => new Command("init").description(
147473
+ "Install the Zero skill and hooks for the agent harnesses on this machine"
147474
+ ).option(
147475
+ "-v, --verbose",
147476
+ "Explain why each install step was taken or skipped"
147477
+ ).option(
147478
+ "--skills-dir <dir...>",
147479
+ "Additional skills directories to install the skill into, for harnesses with bespoke locations (e.g. ~/.kiro/skills). ~/.claude/skills and ~/.agents/skills are always written. Note: `zero uninstall` only cleans the default locations."
147480
+ ).action(async (options) => {
147481
+ await runInit(appContext, options);
147026
147482
  });
147027
147483
 
147028
147484
  // src/commands/review-command.ts
@@ -147288,58 +147744,6 @@ Examples:
147288
147744
 
147289
147745
  // src/commands/search-command.ts
147290
147746
  init_esm_shims();
147291
-
147292
- // src/util/install-banner.ts
147293
- init_esm_shims();
147294
- var colorsEnabled = () => {
147295
- if (process.env.NO_COLOR) return false;
147296
- if (process.env.FORCE_COLOR) return true;
147297
- return Boolean(process.stdout.isTTY);
147298
- };
147299
- var wrap5 = (code, text) => colorsEnabled() ? `\x1B[${code}m${text}\x1B[0m` : text;
147300
- var color = {
147301
- bold: (s) => wrap5("1", s),
147302
- dim: (s) => wrap5("2", s),
147303
- cyan: (s) => wrap5("36", s),
147304
- magenta: (s) => wrap5("35", s),
147305
- green: (s) => wrap5("32", s),
147306
- yellow: (s) => wrap5("33", s),
147307
- red: (s) => wrap5("31", s),
147308
- gray: (s) => wrap5("90", s),
147309
- boldCyan: (s) => wrap5("1;36", s),
147310
- boldMagenta: (s) => wrap5("1;35", s),
147311
- boldGreen: (s) => wrap5("1;32", s),
147312
- boldRed: (s) => wrap5("1;31", s)
147313
- };
147314
- var supportsUnicode = () => {
147315
- if (process.platform !== "win32") return true;
147316
- return Boolean(
147317
- process.env.WT_SESSION || process.env.TERM_PROGRAM === "vscode"
147318
- );
147319
- };
147320
- var SYMBOLS = supportsUnicode() ? { check: "\u2713", arrow: "\u203A", warn: "!" } : { check: "OK", arrow: ">", warn: "!" };
147321
- var stepSuccess = (label, detail) => {
147322
- const line = detail ? ` ${color.boldGreen(SYMBOLS.check)} ${label} ${color.dim(detail)}` : ` ${color.boldGreen(SYMBOLS.check)} ${label}`;
147323
- console.log(line);
147324
- };
147325
- var stepWarn = (label, detail) => {
147326
- const line = ` ${color.yellow(SYMBOLS.warn)} ${label} ${color.dim(detail)}` ;
147327
- console.log(line);
147328
- };
147329
- var stepSkip = (label, detail) => {
147330
- const line = detail ? ` ${color.gray(SYMBOLS.arrow)} ${color.dim(`${label} ${detail}`)}` : ` ${color.gray(SYMBOLS.arrow)} ${color.dim(label)}`;
147331
- console.log(line);
147332
- };
147333
- var stepInfo = (message2) => {
147334
- console.log(` ${color.gray("\xB7")} ${color.dim(message2)}`);
147335
- };
147336
- var sectionDivider = () => {
147337
- console.log("");
147338
- console.log(color.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
147339
- console.log("");
147340
- };
147341
-
147342
- // src/commands/search-command.ts
147343
147747
  var DEFAULT_MAX_COST_USD = "30";
147344
147748
  var formatReviewCount2 = (count) => {
147345
147749
  if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
@@ -147528,8 +147932,12 @@ var AGENT_TOOLS = [
147528
147932
  },
147529
147933
  { name: "Cursor", detectDir: ".cursor", skillsDir: ".cursor/skills" }
147530
147934
  ];
147531
- var HOOK_FILES = ["auto-approve-zero.sh", "zero-context.sh"];
147532
- var ZERO_SANDBOX_DOMAIN = "*.zero.xyz";
147935
+ var HOOK_FILES = [
147936
+ "auto-approve-zero.sh",
147937
+ "zero-context.sh",
147938
+ "ensure-runner.sh"
147939
+ ];
147940
+ var ZERO_SANDBOX_DOMAIN2 = "*.zero.xyz";
147533
147941
  var LEGACY_SKILL_NAMES = ["zero"];
147534
147942
  var removeSkills = (home, verbose = false) => {
147535
147943
  const skillNames = LEGACY_SKILL_NAMES;
@@ -147604,7 +148012,8 @@ var stripZeroFromSettings = (settings, verbose) => {
147604
148012
  if (hooks) {
147605
148013
  changed = removeHookEntries(hooks, "PreToolUse", "auto-approve-zero", verbose) || changed;
147606
148014
  changed = removeHookEntries(hooks, "UserPromptSubmit", "zero-context", verbose) || changed;
147607
- for (const key of ["PreToolUse", "UserPromptSubmit"]) {
148015
+ changed = removeHookEntries(hooks, "SessionStart", "ensure-runner", verbose) || changed;
148016
+ for (const key of ["PreToolUse", "UserPromptSubmit", "SessionStart"]) {
147608
148017
  const arr = hooks[key];
147609
148018
  if (Array.isArray(arr) && arr.length === 0) delete hooks[key];
147610
148019
  }
@@ -147614,11 +148023,11 @@ var stripZeroFromSettings = (settings, verbose) => {
147614
148023
  const network = sandbox?.network && typeof sandbox.network === "object" ? sandbox.network : null;
147615
148024
  if (network && Array.isArray(network.allowedDomains)) {
147616
148025
  const domains = network.allowedDomains;
147617
- const next = domains.filter((d3) => d3 !== ZERO_SANDBOX_DOMAIN);
148026
+ const next = domains.filter((d3) => d3 !== ZERO_SANDBOX_DOMAIN2);
147618
148027
  if (next.length !== domains.length) {
147619
148028
  changed = true;
147620
148029
  if (verbose) {
147621
- stepInfo(`sandbox.network.allowedDomains -= ${ZERO_SANDBOX_DOMAIN}`);
148030
+ stepInfo(`sandbox.network.allowedDomains -= ${ZERO_SANDBOX_DOMAIN2}`);
147622
148031
  }
147623
148032
  if (next.length === 0) {
147624
148033
  delete network.allowedDomains;
@@ -148648,7 +149057,7 @@ var createApp = (appContext) => {
148648
149057
  command: actionCommand.name()
148649
149058
  });
148650
149059
  });
148651
- program2.addCommand(initCommand());
149060
+ program2.addCommand(initCommand(appContext));
148652
149061
  program2.addCommand(uninstallCommand(appContext));
148653
149062
  program2.addCommand(searchCommand(appContext));
148654
149063
  program2.addCommand(getCommand(appContext));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxyz/cli",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",