@duyquangnvx/create-agent-cli 0.10.1 → 0.11.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 (3) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/bin.js +307 -53
  3. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.11.0 (2026-10-22)
8
+
9
+ ### Added
10
+
11
+ - A Dependency install, `install: { mode: "dependency", package, lockstep? }`, for a CLI each project pins as a devDependency. It gets no Built-in `update`, so the CLI may define its own. `doctor`'s `install` row checks that the project's own copy runs, and its `registry` row asks npm at the project root, which reads the project's `.npmrc`, for the latest release, giving `pnpm up --latest …` or `npm install …@latest` with the `lockstep` packages.
12
+
13
+ ### Changed
14
+
15
+ - `doctor`'s `install` and `registry` rows are `skipped` ("not checked: <cli> declares no install") for a CLI that declares no `install`, instead of an `ok` that checked nothing.
16
+
7
17
  ## v0.10.1 (2026-10-22)
8
18
 
9
19
  ### Changed
package/dist/bin.js CHANGED
@@ -3206,7 +3206,7 @@ function registry() {
3206
3206
  return new $ZodRegistry();
3207
3207
  }
3208
3208
  (_a = globalThis).__zod_globalRegistry ?? (_a.__zod_globalRegistry = registry());
3209
- const globalRegistry = globalThis.__zod_globalRegistry;
3209
+ const globalRegistry$1 = globalThis.__zod_globalRegistry;
3210
3210
  //#endregion
3211
3211
  //#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/api.js
3212
3212
  function snapshotChecks(def) {
@@ -3728,7 +3728,7 @@ function initializeContext(params) {
3728
3728
  if (target === "draft-7") target = "draft-07";
3729
3729
  return {
3730
3730
  processors: params.processors ?? {},
3731
- metadataRegistry: params?.metadata ?? globalRegistry,
3731
+ metadataRegistry: params?.metadata ?? globalRegistry$1,
3732
3732
  target,
3733
3733
  unrepresentable: params?.unrepresentable ?? "throw",
3734
3734
  override: params?.override ?? (() => {}),
@@ -4855,6 +4855,7 @@ const initializer = (inst, issues) => {
4855
4855
  }
4856
4856
  });
4857
4857
  };
4858
+ const ZodError = /*@__PURE__*/ $constructor("ZodError", initializer);
4858
4859
  const ZodRealError = /*@__PURE__*/ $constructor("ZodError", initializer, void 0, { Parent: Error });
4859
4860
  //#endregion
4860
4861
  //#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/classic/parse.js
@@ -4959,13 +4960,13 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
4959
4960
  },
4960
4961
  describe(description) {
4961
4962
  const cl = this.clone();
4962
- globalRegistry.add(cl, { description });
4963
+ globalRegistry$1.add(cl, { description });
4963
4964
  return cl;
4964
4965
  },
4965
4966
  meta(...args) {
4966
- if (args.length === 0) return globalRegistry.get(this);
4967
+ if (args.length === 0) return globalRegistry$1.get(this);
4967
4968
  const cl = this.clone();
4968
- globalRegistry.add(cl, args[0]);
4969
+ globalRegistry$1.add(cl, args[0]);
4969
4970
  return cl;
4970
4971
  },
4971
4972
  isOptional() {
@@ -5041,7 +5042,7 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
5041
5042
  return createToJSONSchemaMethod(this, {})(params);
5042
5043
  },
5043
5044
  get description() {
5044
- return globalRegistry.get(this)?.description;
5045
+ return globalRegistry$1.get(this)?.description;
5045
5046
  },
5046
5047
  get _def() {
5047
5048
  return this._zod.def;
@@ -5882,7 +5883,7 @@ function formatRuntimeError(error, format) {
5882
5883
  //#region ../agent-cli/dist/stdin.js
5883
5884
  const STDIN_META = "stdin";
5884
5885
  function isStdinSchema(schema) {
5885
- return globalRegistry.get(schema)?.[STDIN_META] === true;
5886
+ return globalRegistry$1.get(schema)?.[STDIN_META] === true;
5886
5887
  }
5887
5888
  function endOfInputKeys(platform) {
5888
5889
  return platform === "win32" ? "Ctrl-Z then Enter" : "Ctrl-D";
@@ -6813,7 +6814,7 @@ function compareVersions(a, b) {
6813
6814
  return comparePrerelease(left[4], right[4]);
6814
6815
  }
6815
6816
  /** Where `version` stands against `latest`. */
6816
- function standingOf(version, latest) {
6817
+ function standingOf$1(version, latest) {
6817
6818
  const difference = compareVersions(version, latest);
6818
6819
  if (difference < 0) return "behind";
6819
6820
  return difference > 0 ? "ahead" : "current";
@@ -6838,10 +6839,11 @@ const REGISTRY_AUTH = "REGISTRY_AUTH";
6838
6839
  const INSTALL_FAILED = "INSTALL_FAILED";
6839
6840
  const NPM_FAILED = "NPM_FAILED";
6840
6841
  const NO_LATEST = "NO_LATEST";
6841
- function npmContext(cli, install, ctx, node) {
6842
+ function npmContext(cli, registry, ctx, node, cwd) {
6842
6843
  return {
6843
6844
  cliName: cli.name,
6844
- install,
6845
+ registry,
6846
+ cwd,
6845
6847
  env: ctx.env,
6846
6848
  signal: ctx.signal,
6847
6849
  node
@@ -6862,20 +6864,40 @@ const NETWORK_CODES = /* @__PURE__ */ new Set([
6862
6864
  ]);
6863
6865
  const AUTH_CODES = /* @__PURE__ */ new Set(["E401", "E403"]);
6864
6866
  const NPM_CODE = /^npm (?:error|ERR!) code (\S+)$/m;
6867
+ /** The `@scope` of a scoped package name. */
6868
+ function scopeOf(name) {
6869
+ return /^(@[^/]+)\//.exec(name)?.[1];
6870
+ }
6865
6871
  /** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
6866
6872
  function registryFlags({ package: name, registry }) {
6867
6873
  if (registry === NPMJS) return [];
6868
- const scope = /^(@[^/]+)\//.exec(name)?.[1];
6874
+ const scope = scopeOf(name);
6869
6875
  return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
6870
6876
  }
6877
+ /** The registry a Global install declares. */
6878
+ function globalRegistry(install) {
6879
+ const { package: name, registry } = install;
6880
+ return {
6881
+ package: name,
6882
+ label: registry,
6883
+ flags: registryFlags(install),
6884
+ unreachableHint: "check the network or VPN, then run it again",
6885
+ loginHint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again`,
6886
+ missingHint: `check that ${name} is published on ${registry}`
6887
+ };
6888
+ }
6889
+ /** npm for a Global install, run where the CLI was started. */
6890
+ function globalNpm(cli, install, ctx, node) {
6891
+ return npmContext(cli, globalRegistry(install), ctx, node, process.cwd());
6892
+ }
6871
6893
  /** The command a person runs to install the CLI globally from its registry. */
6872
- function installCommandLine(install, version = "latest") {
6894
+ function installCommandLine(registry, version = "latest") {
6873
6895
  return [
6874
6896
  "npm",
6875
6897
  "install",
6876
6898
  "--global",
6877
- `${install.package}@${version}`,
6878
- ...registryFlags(install)
6899
+ `${registry.package}@${version}`,
6900
+ ...registry.flags
6879
6901
  ].join(" ");
6880
6902
  }
6881
6903
  function npmOf(ctx, windows) {
@@ -6900,7 +6922,7 @@ function npmOf(ctx, windows) {
6900
6922
  function spawnNpm(args, ctx, signal) {
6901
6923
  const npm = npmOf(ctx, process.platform === "win32");
6902
6924
  return spawnChild(npm.path, args, {
6903
- cwd: process.cwd(),
6925
+ cwd: ctx.cwd,
6904
6926
  env: npm.env,
6905
6927
  signal: signal ?? new AbortController().signal,
6906
6928
  detached: signal === null
@@ -6911,24 +6933,24 @@ function tail$1(stderr) {
6911
6933
  return lines === "" ? "" : `\n${lines}`;
6912
6934
  }
6913
6935
  function failureOf(call, answer, ctx) {
6914
- const { registry, package: name } = ctx.install;
6936
+ const { label: registry, package: name, loginHint, unreachableHint } = ctx.registry;
6915
6937
  const code = NPM_CODE.exec(answer.stderr)?.[1];
6916
6938
  const output = tail$1(answer.stderr);
6917
6939
  if (code !== void 0 && NETWORK_CODES.has(code)) return new CliError({
6918
6940
  code: REGISTRY_UNREACHABLE,
6919
6941
  message: `npm could not reach ${registry} (${code})${output}`,
6920
- hint: "check the network or VPN, then run it again"
6942
+ hint: unreachableHint
6921
6943
  });
6922
6944
  if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
6923
6945
  code: REGISTRY_AUTH,
6924
6946
  message: `${registry} refused npm's credentials (${code})${output}`,
6925
- hint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again`
6947
+ hint: loginHint
6926
6948
  });
6927
6949
  if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
6928
6950
  if (call === "install") return new CliError({
6929
6951
  code: INSTALL_FAILED,
6930
6952
  message: `npm could not install ${name}${code === void 0 ? "" : ` (${code})`}${output}`,
6931
- hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.install)}`
6953
+ hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.registry)}`
6932
6954
  });
6933
6955
  return new CliError({
6934
6956
  code: NPM_FAILED,
@@ -6940,7 +6962,7 @@ function noLatest(ctx, message) {
6940
6962
  return new CliError({
6941
6963
  code: NO_LATEST,
6942
6964
  message,
6943
- hint: `check that ${ctx.install.package} is published on ${ctx.install.registry}`
6965
+ hint: ctx.registry.missingHint
6944
6966
  });
6945
6967
  }
6946
6968
  /**
@@ -6991,18 +7013,18 @@ async function installGlobally(version, ctx) {
6991
7013
  "--global",
6992
7014
  "--no-audit",
6993
7015
  "--no-fund",
6994
- `${ctx.install.package}@${version}`,
6995
- ...registryFlags(ctx.install)
7016
+ `${ctx.registry.package}@${version}`,
7017
+ ...ctx.registry.flags
6996
7018
  ], ctx, null);
6997
7019
  }
6998
7020
  async function latestVersion(ctx) {
6999
- const { package: name, registry } = ctx.install;
7021
+ const { package: name, label: registry, flags } = ctx.registry;
7000
7022
  const answer = await runNpm("view", [
7001
7023
  "view",
7002
7024
  `${name}@latest`,
7003
7025
  "version",
7004
7026
  "--json",
7005
- ...registryFlags(ctx.install)
7027
+ ...flags
7006
7028
  ], ctx);
7007
7029
  const version = versionInViewAnswer(answer);
7008
7030
  if (version === void 0) throw noLatest(ctx, `npm answered no version for ${name}@latest on ${registry}: ${answer.trim()}`);
@@ -7019,7 +7041,7 @@ function notGlobal(ctx, message) {
7019
7041
  return new CliError({
7020
7042
  code: NOT_GLOBAL_INSTALL,
7021
7043
  message,
7022
- hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.install)}`
7044
+ hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.registry)}`
7023
7045
  });
7024
7046
  }
7025
7047
  async function realpathOrUndefined(path) {
@@ -7028,6 +7050,7 @@ async function realpathOrUndefined(path) {
7028
7050
  throw error;
7029
7051
  });
7030
7052
  }
7053
+ /** Whether `path` lies strictly inside `dir`. */
7031
7054
  function isInside(dir, path) {
7032
7055
  const inner = relative(dir, path);
7033
7056
  return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
@@ -7039,7 +7062,7 @@ function isInside(dir, path) {
7039
7062
  async function globalInstallOf(bin, ctx) {
7040
7063
  if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
7041
7064
  const prefix = await globalPrefix(ctx);
7042
- const packageDir = join(globalModulesDir(prefix), ctx.install.package);
7065
+ const packageDir = join(globalModulesDir(prefix), ctx.registry.package);
7043
7066
  const installed = await realpathOrUndefined(packageDir);
7044
7067
  const here = await realpathOrUndefined(bin) ?? bin;
7045
7068
  if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
@@ -7050,6 +7073,196 @@ async function globalInstallOf(bin, ctx) {
7050
7073
  };
7051
7074
  }
7052
7075
  //#endregion
7076
+ //#region ../agent-cli/dist/built-ins/dependency-install.js
7077
+ const MANAGERS = [
7078
+ "pnpm",
7079
+ "npm",
7080
+ "yarn",
7081
+ "bun"
7082
+ ];
7083
+ const LOCKFILES = [
7084
+ ["pnpm-lock.yaml", "pnpm"],
7085
+ ["package-lock.json", "npm"],
7086
+ ["yarn.lock", "yarn"],
7087
+ ["bun.lock", "bun"],
7088
+ ["bun.lockb", "bun"]
7089
+ ];
7090
+ const LINES = {
7091
+ pnpm: {
7092
+ upgrade: (packages) => `pnpm up --latest ${packages.join(" ")}`,
7093
+ add: (packages) => `pnpm add -D ${packages.join(" ")}`,
7094
+ reinstall: "pnpm install",
7095
+ exec: (cli) => `pnpm exec ${cli}`
7096
+ },
7097
+ npm: {
7098
+ upgrade: (packages) => `npm install ${packages.map((name) => `${name}@latest`).join(" ")}`,
7099
+ add: (packages) => `npm install -D ${packages.join(" ")}`,
7100
+ reinstall: "npm install",
7101
+ exec: (cli) => `npx ${cli}`
7102
+ }
7103
+ };
7104
+ const manifestSchema$1 = object({ packageManager: string().optional() });
7105
+ const packageSchema = object({ version: string() });
7106
+ /** `dir` and every folder above it, nearest first. */
7107
+ function ancestorsOf(dir) {
7108
+ const parent = dirname(dir);
7109
+ return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
7110
+ }
7111
+ async function exists(path) {
7112
+ try {
7113
+ await stat(path);
7114
+ return true;
7115
+ } catch (error) {
7116
+ if (isMissingFile(error)) return false;
7117
+ throw error;
7118
+ }
7119
+ }
7120
+ async function declaredManager(dir) {
7121
+ let text;
7122
+ try {
7123
+ text = await readFile(join(dir, "package.json"), "utf8");
7124
+ } catch (error) {
7125
+ if (isMissingFile(error)) return void 0;
7126
+ throw error;
7127
+ }
7128
+ let manifest;
7129
+ try {
7130
+ manifest = JSON.parse(text);
7131
+ } catch {
7132
+ return;
7133
+ }
7134
+ const name = manifestSchema$1.safeParse(manifest).data?.packageManager?.split("@")[0];
7135
+ return MANAGERS.find((manager) => manager === name);
7136
+ }
7137
+ async function lockfileRootOf(cwd) {
7138
+ for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await exists(join(dir, file))) return {
7139
+ dir,
7140
+ manager: await declaredManager(dir) ?? kind
7141
+ };
7142
+ }
7143
+ async function versionIn(manifest) {
7144
+ try {
7145
+ return packageSchema.parse(JSON.parse(await readFile(manifest, "utf8"))).version;
7146
+ } catch (error) {
7147
+ if (error instanceof SyntaxError || error instanceof ZodError) return { reason: messageOf(error) };
7148
+ throw error;
7149
+ }
7150
+ }
7151
+ async function standingOf(install, bin, cwd) {
7152
+ if (bin === void 0) return { kind: "source" };
7153
+ for (const projectDir of ancestorsOf(cwd)) {
7154
+ const folder = join(projectDir, "node_modules", install.package);
7155
+ const manifest = join(folder, "package.json");
7156
+ if (!await exists(manifest)) continue;
7157
+ const version = await versionIn(manifest);
7158
+ if (typeof version !== "string") return {
7159
+ kind: "unreadable",
7160
+ manifest,
7161
+ ...version
7162
+ };
7163
+ const copy = {
7164
+ projectDir,
7165
+ realDir: await realpath(folder),
7166
+ version
7167
+ };
7168
+ if (!copy.realDir.split(sep).includes("node_modules")) return {
7169
+ kind: "linked",
7170
+ copy
7171
+ };
7172
+ const runningFrom = await realpath(bin).catch((error) => {
7173
+ if (isMissingFile(error)) return bin;
7174
+ throw error;
7175
+ });
7176
+ return isInside(copy.realDir, runningFrom) ? {
7177
+ kind: "installed",
7178
+ copy
7179
+ } : {
7180
+ kind: "other",
7181
+ copy,
7182
+ runningFrom
7183
+ };
7184
+ }
7185
+ return { kind: "missing" };
7186
+ }
7187
+ async function surveyDependency(install, bin, cwd) {
7188
+ const [root, standing] = await Promise.all([lockfileRootOf(cwd), standingOf(install, bin, cwd)]);
7189
+ return {
7190
+ cwd,
7191
+ root,
7192
+ standing
7193
+ };
7194
+ }
7195
+ function packagesOf(install) {
7196
+ return [install.package, ...install.lockstep ?? []];
7197
+ }
7198
+ function isUnchecked(standing) {
7199
+ return standing.kind === "source" || standing.kind === "linked";
7200
+ }
7201
+ function skippedFor(cli, install, standing) {
7202
+ return {
7203
+ state: "skipped",
7204
+ message: standing.kind === "source" ? `not checked: ${cli.name} runs from source, not from an install` : `not checked: ${install.package} links to ${standing.copy.realDir}, so the workspace decides its version`
7205
+ };
7206
+ }
7207
+ function problem(message, fix) {
7208
+ return fix === void 0 ? {
7209
+ state: "problem",
7210
+ message
7211
+ } : {
7212
+ state: "problem",
7213
+ message,
7214
+ fix
7215
+ };
7216
+ }
7217
+ /** The `install` row of a Dependency install. */
7218
+ function dependencyInstallFinding(cli, install, { cwd, root, standing }) {
7219
+ const lines = root === void 0 ? void 0 : LINES[root.manager];
7220
+ switch (standing.kind) {
7221
+ case "source":
7222
+ case "linked": return skippedFor(cli, install, standing);
7223
+ case "missing": return problem(`no package at or above ${cwd} depends on ${install.package}`, lines?.add(packagesOf(install)));
7224
+ case "unreadable": return problem(`${standing.manifest} is not a package.json with a version: ${standing.reason}`, lines?.reinstall);
7225
+ case "other": {
7226
+ const { copy, runningFrom } = standing;
7227
+ return problem(`${cli.name} runs from ${runningFrom}, not from this project's ${install.package} ${copy.version} at ${copy.realDir}`, lines?.exec(cli.name));
7228
+ }
7229
+ case "installed": {
7230
+ const { copy } = standing;
7231
+ const where = root === void 0 ? copy.projectDir : `${root.dir} with ${root.manager}`;
7232
+ return {
7233
+ state: "ok",
7234
+ message: `${install.package} ${copy.version}, installed in ${where}`
7235
+ };
7236
+ }
7237
+ }
7238
+ }
7239
+ function registryOf({ package: name }, root) {
7240
+ const npmrc = root === void 0 ? "the project's .npmrc" : join(root, ".npmrc");
7241
+ const scope = scopeOf(name);
7242
+ return {
7243
+ package: name,
7244
+ label: `the registry npm uses for ${name}`,
7245
+ flags: [],
7246
+ unreachableHint: scope === void 0 ? "check the network or VPN, then run it again" : `check the network or VPN and the ${scope}:registry that ${npmrc} sets, then run it again`,
7247
+ loginHint: `log in to it (npm login${scope === void 0 ? "" : ` --scope=${scope}`}) or set its token in ${npmrc}, then run it again`,
7248
+ missingHint: scope === void 0 ? `check that ${name} is published on the registry ${npmrc} names` : `check that ${name} is published, and that ${npmrc} maps ${scope} to its registry: ${scope}:registry=<url>`
7249
+ };
7250
+ }
7251
+ function adviceFor(install, root, latest) {
7252
+ const packages = packagesOf(install);
7253
+ const lines = root === void 0 ? void 0 : LINES[root.manager];
7254
+ if (lines !== void 0) return { fix: lines.upgrade(packages) };
7255
+ return { note: `move ${packages.length === 1 ? packages[0] : `${packages.slice(0, -1).join(", ")} and ${packages.at(-1)}`} to ${latest} with ${root?.manager ?? "the project's package manager"}` };
7256
+ }
7257
+ function dependencyRelease(cli, install, { cwd, root, standing }) {
7258
+ if (isUnchecked(standing)) return { skipped: skippedFor(cli, install, standing) };
7259
+ return {
7260
+ registry: registryOf(install, root?.dir),
7261
+ cwd: root?.dir ?? cwd,
7262
+ advise: (latest) => adviceFor(install, root, latest)
7263
+ };
7264
+ }
7265
+ //#endregion
7053
7266
  //#region ../agent-cli/dist/built-ins/skills.js
7054
7267
  const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7055
7268
  const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
@@ -7083,7 +7296,7 @@ async function copyAt(path, version) {
7083
7296
  return {
7084
7297
  path,
7085
7298
  version: stated,
7086
- state: standingOf(stated, version)
7299
+ state: standingOf$1(stated, version)
7087
7300
  };
7088
7301
  }
7089
7302
  async function skillCopies(cliName, version, { cwd, env }) {
@@ -7123,6 +7336,12 @@ function ok(message) {
7123
7336
  message
7124
7337
  };
7125
7338
  }
7339
+ function noInstall(cli) {
7340
+ return {
7341
+ state: "skipped",
7342
+ message: `not checked: ${cli.name} declares no install`
7343
+ };
7344
+ }
7126
7345
  function buildRow(cli) {
7127
7346
  return {
7128
7347
  name: "build",
@@ -7189,41 +7408,75 @@ function projectRow({ file }, read) {
7189
7408
  }
7190
7409
  };
7191
7410
  }
7192
- function installRow(cli, { bin, node }) {
7411
+ function surveyOnce(bin) {
7412
+ let survey;
7413
+ return (install, cwd) => {
7414
+ survey ??= surveyDependency(install, bin, cwd);
7415
+ return survey;
7416
+ };
7417
+ }
7418
+ function installRow(cli, { bin, node }, survey) {
7193
7419
  return {
7194
7420
  name: "install",
7195
7421
  advisory: true,
7196
7422
  run: async (ctx) => {
7197
7423
  const { install } = cli;
7198
- if (install === void 0) return ok(`${cli.name} declares no install, so it has no \`update\``);
7424
+ if (install === void 0) return noInstall(cli);
7425
+ if (install.mode === "dependency") return dependencyInstallFinding(cli, install, await survey(install, ctx.cwd));
7426
+ const npm = globalNpm(cli, install, ctx, node);
7199
7427
  try {
7200
- return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx, node))).prefix}`);
7428
+ return ok(`a global npm install under ${(await globalInstallOf(bin, npm)).prefix}`);
7201
7429
  } catch (error) {
7202
7430
  if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
7203
7431
  state: "problem",
7204
7432
  message: error.message,
7205
- fix: installCommandLine(install)
7433
+ fix: installCommandLine(npm.registry)
7206
7434
  };
7207
7435
  throw error;
7208
7436
  }
7209
7437
  }
7210
7438
  };
7211
7439
  }
7212
- function registryRow(cli, node) {
7440
+ async function releaseLookupOf(cli, install, ctx, node, survey) {
7441
+ if (install.mode === "global") {
7442
+ const fix = `${cli.name} update`;
7443
+ return {
7444
+ npm: globalNpm(cli, install, ctx, node),
7445
+ advise: () => ({ fix })
7446
+ };
7447
+ }
7448
+ const release = dependencyRelease(cli, install, await survey(install, ctx.cwd));
7449
+ if ("skipped" in release) return release;
7450
+ return {
7451
+ npm: npmContext(cli, release.registry, ctx, node, release.cwd),
7452
+ advise: release.advise
7453
+ };
7454
+ }
7455
+ function registryRow(cli, { node }, survey) {
7213
7456
  return {
7214
7457
  name: "registry",
7215
7458
  advisory: true,
7216
7459
  run: async (ctx) => {
7217
7460
  const { install } = cli;
7218
- if (install === void 0) return ok(`${cli.name} declares no registry to compare this build with`);
7219
- const latest = await latestVersion(npmContext(cli, install, ctx, node));
7220
- const standing = standingOf(cli.version, latest);
7221
- const head = `${install.registry} has ${latest} as latest: this build`;
7222
- if (standing === "behind") return {
7223
- state: "problem",
7224
- message: `${head} (${cli.version}) is older`,
7225
- fix: `${cli.name} update`
7226
- };
7461
+ if (install === void 0) return noInstall(cli);
7462
+ const lookup = await releaseLookupOf(cli, install, ctx, node, survey);
7463
+ if ("skipped" in lookup) return lookup.skipped;
7464
+ const { npm, advise } = lookup;
7465
+ const latest = await latestVersion(npm);
7466
+ const standing = standingOf$1(cli.version, latest);
7467
+ const head = `${npm.registry.label} has ${latest} as latest: this build`;
7468
+ if (standing === "behind") {
7469
+ const message = `${head} (${cli.version}) is older`;
7470
+ const advice = advise(latest);
7471
+ return "fix" in advice ? {
7472
+ state: "problem",
7473
+ message,
7474
+ fix: advice.fix
7475
+ } : {
7476
+ state: "problem",
7477
+ message: `${message}; ${advice.note}`
7478
+ };
7479
+ }
7227
7480
  return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
7228
7481
  }
7229
7482
  };
@@ -7254,14 +7507,15 @@ function skillRow(cli) {
7254
7507
  }
7255
7508
  /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
7256
7509
  function doctorChecks(cli, launch, read) {
7510
+ const survey = surveyOnce(launch.bin);
7257
7511
  return [
7258
7512
  buildRow(cli),
7259
7513
  nodeRow(cli),
7260
7514
  ...cli.project === void 0 ? [] : [projectRow(cli.project, read)],
7261
7515
  ...cli.doctor ?? [],
7262
7516
  ...toolRows(cli.tools ?? [], cli.commands),
7263
- installRow(cli, launch),
7264
- registryRow(cli, launch.node),
7517
+ installRow(cli, launch, survey),
7518
+ registryRow(cli, launch, survey),
7265
7519
  skillRow(cli)
7266
7520
  ];
7267
7521
  }
@@ -7592,7 +7846,7 @@ const updateSchema = object({
7592
7846
  next: array(string())
7593
7847
  });
7594
7848
  function nothingToInstall({ from, latest }) {
7595
- return standingOf(from, latest) === "current" ? `already on ${from}, the latest release: nothing to install` : `${from} is newer than the latest release ${latest}: nothing to install`;
7849
+ return standingOf$1(from, latest) === "current" ? `already on ${from}, the latest release: nothing to install` : `${from} is newer than the latest release ${latest}: nothing to install`;
7596
7850
  }
7597
7851
  function renderUpgrade(upgrade) {
7598
7852
  return "steps" in upgrade ? [`\nupgrade steps for ${upgrade.version}:`, ...upgrade.steps.map((step) => `- ${step}`)].join("\n") : `\nchanges in ${upgrade.version}:\n${upgrade.notes}`;
@@ -7633,7 +7887,7 @@ function updateCommand(cli, install, { bin, node }) {
7633
7887
  effect: "write",
7634
7888
  output: updateSchema,
7635
7889
  plan: async (_input, ctx) => {
7636
- const npm = npmContext(cli, install, ctx, node);
7890
+ const npm = globalNpm(cli, install, ctx, node);
7637
7891
  const copy = await globalInstallOf(bin, npm);
7638
7892
  return {
7639
7893
  package: install.package,
@@ -7645,7 +7899,7 @@ function updateCommand(cli, install, { bin, node }) {
7645
7899
  },
7646
7900
  apply: async (plan, ctx) => {
7647
7901
  const { from, latest, prefix } = plan;
7648
- if (standingOf(from, latest) !== "behind") return {
7902
+ if (standingOf$1(from, latest) !== "behind") return {
7649
7903
  from,
7650
7904
  latest,
7651
7905
  installed: null,
@@ -7654,7 +7908,7 @@ function updateCommand(cli, install, { bin, node }) {
7654
7908
  };
7655
7909
  ctx.signal.throwIfAborted();
7656
7910
  ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
7657
- await ctx.runToEnd(`installing ${plan.package}@${latest}`, () => installGlobally(latest, npmContext(cli, install, ctx, node)));
7911
+ await ctx.runToEnd(`installing ${plan.package}@${latest}`, () => installGlobally(latest, globalNpm(cli, install, ctx, node)));
7658
7912
  const update = {
7659
7913
  from,
7660
7914
  latest,
@@ -7672,16 +7926,16 @@ function updateCommand(cli, install, { bin, node }) {
7672
7926
  return update;
7673
7927
  },
7674
7928
  render: (update) => renderUpdate(cli, install, update),
7675
- renderPlan: (plan) => standingOf(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan)
7929
+ renderPlan: (plan) => standingOf$1(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan)
7676
7930
  });
7677
7931
  }
7678
7932
  //#endregion
7679
7933
  //#region ../agent-cli/dist/built-ins/commands.js
7680
- /** The Commands the Runtime adds to a multi-Command CLI: `update` only when it declares an install. */
7934
+ /** The Commands the Runtime adds to a multi-Command CLI: `update` only for a Global install. */
7681
7935
  function builtInCommands(cli, launch) {
7682
7936
  return {
7683
7937
  doctor: doctorCommand(cli, launch),
7684
- ...cli.install === void 0 ? {} : { update: updateCommand(cli, cli.install, launch) }
7938
+ ...cli.install?.mode === "global" ? { update: updateCommand(cli, cli.install, launch) } : {}
7685
7939
  };
7686
7940
  }
7687
7941
  //#endregion
@@ -9421,14 +9675,14 @@ async function runProcess(cli) {
9421
9675
  }
9422
9676
  //#endregion
9423
9677
  //#region package.json
9424
- var version$1 = "0.10.1";
9678
+ var version$1 = "0.11.0";
9425
9679
  var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
9426
9680
  //#endregion
9427
9681
  //#region src/build.ts
9428
9682
  const build = {
9429
- "sha": "0e1aa22",
9683
+ "sha": "b418c6b",
9430
9684
  "dirty": false,
9431
- "builtAt": "2026-10-22T01:43:18.000Z"
9685
+ "builtAt": "2026-10-22T09:00:08.000Z"
9432
9686
  };
9433
9687
  //#endregion
9434
9688
  //#region src/steps.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duyquangnvx/create-agent-cli",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -19,7 +19,7 @@
19
19
  "access": "public"
20
20
  },
21
21
  "devDependencies": {
22
- "@duyquangnvx/agent-cli": "0.10.1",
22
+ "@duyquangnvx/agent-cli": "0.11.0",
23
23
  "tsdown": "0.23.0",
24
24
  "zod": "4.6.7"
25
25
  },