@duyquangnvx/create-agent-cli 0.21.0 → 0.23.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.23.0 (2026-11-03)
8
+
9
+ ### Added
10
+
11
+ - A Global install of a CLI with several Commands runs a Release check: at most once a day a run asks the Install registry, in a detached process, for the latest release, and later runs give the Notice ``<cli> X.Y.Z is out, this is A.B.C; run `<cli> update` `` while the CLI is behind. No run waits on it. `CI`, or `<NAME>_NO_RELEASE_CHECK=1` for one CLI, turns it off; it keeps its answer in `$XDG_CACHE_HOME/<name>/release-check.json`, by default under `~/.cache`.
12
+
13
+ ## v0.22.0 (2026-11-02)
14
+
15
+ ### Added
16
+
17
+ - The hint of `UNKNOWN_OPTION`, `UNKNOWN_COMMAND` and `INVALID_INPUT` names each Skill copy whose `metadata.version` differs from the build's, for a Skill Hub or a Packaged skill, and says to run `doctor` to update it. A CLI's skill can drop its line on a flag or Command it names that the CLI does not know.
18
+ - The scaffolded setup reference of a Global install says what to do when the user installed the CLI but the shell finds none, under nvm. A CLI's skill can drop its own `command not found` row.
19
+
20
+ ### Changed
21
+
22
+ - `REGISTRY_AUTH` from npm's `E403` under a Coding agent hints that the agent's sandbox may refuse the registry: the user types `!` and runs it again, or allows the registry in the sandbox's network list, and logs in only if it fails outside the sandbox too. `update`, the install and `doctor`'s `registry` row all give it. A CLI's skill can drop its row saying that `REGISTRY_AUTH` is no refused login, and its `INTERNAL` row too: that hint already names `<NAME>_DEBUG=1`.
23
+ - The scaffolded skill drops what the agent sees anyway: `SKILL.md` loses its When to use section, which belongs in the description, its paragraph on `--json`, `--dry-run` and `--confirm`, and its description of the error format; the setup reference's Check no longer describes `doctor`'s rows. The shipping guide says a skill line earns its place only when no `--help`, output, hint or `doctor` row says it.
24
+
7
25
  ## v0.21.0 (2026-10-29)
8
26
 
9
27
  ### Added
package/dist/bin.js CHANGED
@@ -5959,8 +5959,12 @@ async function orMissing(pending) {
5959
5959
  function isNothingThere(error) {
5960
5960
  return isMissingFile(error) || errnoOf(error) === "ENOTDIR";
5961
5961
  }
5962
+ /** The CLI's own environment variable, such as `TOOL_DEBUG` for `cliVariable("tool", "DEBUG")`. */
5963
+ function cliVariable(cliName, suffix) {
5964
+ return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_${suffix}`;
5965
+ }
5962
5966
  function debugVariable(cliName) {
5963
- return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_DEBUG`;
5967
+ return cliVariable(cliName, "DEBUG");
5964
5968
  }
5965
5969
  function messageOf(error) {
5966
5970
  return error instanceof Error ? error.message : String(error);
@@ -7081,9 +7085,9 @@ function secretRows(secrets, commands) {
7081
7085
  /** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
7082
7086
  function buildStamp() {
7083
7087
  return {
7084
- "sha": "75fd8d7",
7088
+ "sha": "71583a7",
7085
7089
  "dirty": false,
7086
- "builtAt": "2026-10-29T03:29:07.000Z"
7090
+ "builtAt": "2026-11-03T06:40:47.000Z"
7087
7091
  };
7088
7092
  }
7089
7093
  /** The first line of `--version`: the version, and the commit and time of a stamped build. */
@@ -7092,6 +7096,40 @@ function versionLine({ version }) {
7092
7096
  return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
7093
7097
  }
7094
7098
  //#endregion
7099
+ //#region ../agent-cli/dist/context/agent.js
7100
+ const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
7101
+ const isSet = (name) => (env) => (env[name] ?? "") !== "";
7102
+ const equals = (name, value) => (env) => env[name] === value;
7103
+ const MARKERS = [
7104
+ (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
7105
+ equals("AGENT", "amp"),
7106
+ isSet("CODEX_SANDBOX"),
7107
+ isSet("CODEX_CI"),
7108
+ isSet("CODEX_THREAD_ID"),
7109
+ isSet("GEMINI_CLI"),
7110
+ isSet("COPILOT_CLI"),
7111
+ isSet("OPENCODE"),
7112
+ isSet("ANTIGRAVITY_AGENT"),
7113
+ isSet("AUGMENT_AGENT"),
7114
+ isSet("REPL_ID"),
7115
+ isSet("CLAUDE_CODE_IS_COWORK"),
7116
+ isSet("CLAUDECODE"),
7117
+ isSet("CLAUDE_CODE"),
7118
+ isSet("CURSOR_TRACE_ID"),
7119
+ isSet("CURSOR_AGENT"),
7120
+ equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
7121
+ equals("TERM_PROGRAM", "kiro"),
7122
+ (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
7123
+ isSet("GOOSE_PROVIDER")
7124
+ ];
7125
+ /**
7126
+ * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
7127
+ * in a PTY, so a TTY alone doesn't mean a person is watching.
7128
+ */
7129
+ function isAgent(env) {
7130
+ return MARKERS.some((marker) => marker(env));
7131
+ }
7132
+ //#endregion
7095
7133
  //#region ../agent-cli/dist/built-ins/semver.js
7096
7134
  const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
7097
7135
  const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
@@ -7231,8 +7269,14 @@ function tail$1(stderr) {
7231
7269
  const lines = lastLines(stderr, STDERR_TAIL_LINES);
7232
7270
  return lines === "" ? "" : `\n${lines}`;
7233
7271
  }
7272
+ function authHint(code, ctx) {
7273
+ const { label: registry, loginHint } = ctx.registry;
7274
+ const login = `${loginHint}, then ${RUN_AGAIN}`;
7275
+ if (code !== "E403" || !isAgent(ctx.env)) return login;
7276
+ return `a Coding agent's sandbox may refuse ${registry} with a 403: ask your user to type ! and ${RUN_AGAIN}, or to allow ${registry} in the sandbox's network list; if it fails outside the sandbox too, ${login}`;
7277
+ }
7234
7278
  function failureOf(call, answer, ctx) {
7235
- const { label: registry, package: name, loginHint, unreachableHint } = ctx.registry;
7279
+ const { label: registry, package: name, unreachableHint } = ctx.registry;
7236
7280
  const code = NPM_CODE.exec(answer.stderr)?.[1];
7237
7281
  const output = tail$1(answer.stderr);
7238
7282
  const next = [ctx.rerun];
@@ -7245,7 +7289,7 @@ function failureOf(call, answer, ctx) {
7245
7289
  if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
7246
7290
  code: REGISTRY_AUTH,
7247
7291
  message: `${registry} refused npm's credentials (${code})${output}`,
7248
- hint: `${loginHint}, then ${RUN_AGAIN}`,
7292
+ hint: authHint(code, ctx),
7249
7293
  next
7250
7294
  });
7251
7295
  if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
@@ -7321,15 +7365,18 @@ async function installGlobally(version, ctx) {
7321
7365
  ...ctx.registry.flags
7322
7366
  ], ctx, null);
7323
7367
  }
7324
- async function latestVersion(ctx) {
7325
- const { package: name, label: registry, flags } = ctx.registry;
7326
- const answer = await runNpm("view", [
7368
+ function latestViewArgs({ package: name, flags }) {
7369
+ return [
7327
7370
  "view",
7328
7371
  `${name}@latest`,
7329
7372
  "version",
7330
7373
  "--json",
7331
7374
  ...flags
7332
- ], ctx);
7375
+ ];
7376
+ }
7377
+ async function latestVersion(ctx) {
7378
+ const { package: name, label: registry } = ctx.registry;
7379
+ const answer = await runNpm("view", latestViewArgs(ctx.registry), ctx);
7333
7380
  const version = versionInViewAnswer(answer);
7334
7381
  if (version === void 0) throw noLatest(ctx, `npm answered no version for ${name}@latest on ${registry}: ${answer.trim()}`);
7335
7382
  return version;
@@ -7571,7 +7618,8 @@ function dependencyInstall(cli, install) {
7571
7618
  runAs,
7572
7619
  builtIns: () => ({}),
7573
7620
  exitClasses: [],
7574
- check: (launch) => checkOf$1(cli, install, runAs, launch)
7621
+ check: (launch) => checkOf$1(cli, install, runAs, launch),
7622
+ notice: async () => {}
7575
7623
  };
7576
7624
  }
7577
7625
  //#endregion
@@ -7669,6 +7717,7 @@ function hubSkill(cli, from) {
7669
7717
  run: async (ctx) => findingOf(await skillCopies(cli.name, cli.version, ctx))
7670
7718
  }],
7671
7719
  notice: async () => {},
7720
+ differing: async (place) => (await skillCopies(cli.name, cli.version, place)).filter(({ state }) => state !== "current" && state !== "unreadable").map(describeCopy),
7672
7721
  nextAt: async (version, ctx) => {
7673
7722
  const copies = await skillCopies(cli.name, version, ctx);
7674
7723
  const fix = fixFor(cli.name, copies, from);
@@ -7751,40 +7800,6 @@ function agentsRow(agents, install) {
7751
7800
  };
7752
7801
  }
7753
7802
  //#endregion
7754
- //#region ../agent-cli/dist/context/agent.js
7755
- const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
7756
- const isSet = (name) => (env) => (env[name] ?? "") !== "";
7757
- const equals = (name, value) => (env) => env[name] === value;
7758
- const MARKERS = [
7759
- (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
7760
- equals("AGENT", "amp"),
7761
- isSet("CODEX_SANDBOX"),
7762
- isSet("CODEX_CI"),
7763
- isSet("CODEX_THREAD_ID"),
7764
- isSet("GEMINI_CLI"),
7765
- isSet("COPILOT_CLI"),
7766
- isSet("OPENCODE"),
7767
- isSet("ANTIGRAVITY_AGENT"),
7768
- isSet("AUGMENT_AGENT"),
7769
- isSet("REPL_ID"),
7770
- isSet("CLAUDE_CODE_IS_COWORK"),
7771
- isSet("CLAUDECODE"),
7772
- isSet("CLAUDE_CODE"),
7773
- isSet("CURSOR_TRACE_ID"),
7774
- isSet("CURSOR_AGENT"),
7775
- equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
7776
- equals("TERM_PROGRAM", "kiro"),
7777
- (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
7778
- isSet("GOOSE_PROVIDER")
7779
- ];
7780
- /**
7781
- * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
7782
- * in a PTY, so a TTY alone doesn't mean a person is watching.
7783
- */
7784
- function isAgent(env) {
7785
- return MARKERS.some((marker) => marker(env));
7786
- }
7787
- //#endregion
7788
7803
  //#region ../agent-cli/dist/context/errno.js
7789
7804
  const PERMISSION_DENIED = "PERMISSION_DENIED";
7790
7805
  const DISK_FULL = "DISK_FULL";
@@ -8305,6 +8320,15 @@ function packagedSkill(cli, { packaged: folder, agents: text }, install) {
8305
8320
  run: (ctx) => findingOf(cli, folder, install, ctx)
8306
8321
  }, ...agents === void 0 ? [] : [agentsRow(agents, install)]],
8307
8322
  notice: (launch, ctx) => notice(cli, folder, install, launch, ctx),
8323
+ differing: async ({ cwd, packageRoot }) => {
8324
+ const { copy } = await skillCopyOf(cli, cwd);
8325
+ const { copyVersion, packageVersion, same } = await skillVersionsOf(join(packageRoot, folder), copy);
8326
+ return copyVersion === void 0 || same ? [] : [compared(cli.name, {
8327
+ copy,
8328
+ copyVersion,
8329
+ packageVersion
8330
+ }, false)];
8331
+ },
8308
8332
  nextAt: async (_version, ctx) => {
8309
8333
  const { fix } = await findingOf(cli, folder, install, ctx);
8310
8334
  return fix === void 0 ? [] : [fix];
@@ -8672,6 +8696,127 @@ function updateCommand(cli, install, skill, { bin, node }) {
8672
8696
  });
8673
8697
  }
8674
8698
  //#endregion
8699
+ //#region ../agent-cli/dist/built-ins/install/release-check.js
8700
+ const CHECK_INTERVAL_MS = 864e5;
8701
+ const checkSchema = object({
8702
+ checkedAt: number(),
8703
+ latest: string().optional(),
8704
+ error: string().optional()
8705
+ });
8706
+ const CHECK_SCRIPT = `
8707
+ const { spawnSync } = require("node:child_process");
8708
+ const { mkdtempSync, renameSync, rmSync, writeFileSync } = require("node:fs");
8709
+ const { tmpdir } = require("node:os");
8710
+ const { join } = require("node:path");
8711
+ const [file, checkedAt, previous, npm, ...args] = process.argv.slice(1);
8712
+ const windows = process.platform === "win32";
8713
+ const cache = mkdtempSync(join(tmpdir(), "release-check-"));
8714
+ const write = (check) => {
8715
+ const temp = file + "." + process.pid + ".tmp";
8716
+ writeFileSync(temp, JSON.stringify({ checkedAt: Number(checkedAt), ...check }));
8717
+ renameSync(temp, file);
8718
+ };
8719
+ const latestOf = (stdout) => {
8720
+ try {
8721
+ const parsed = JSON.parse(stdout);
8722
+ const latest = Array.isArray(parsed) ? parsed.at(-1) : parsed;
8723
+ return typeof latest === "string" && /^\\d+\\.\\d+\\.\\d+/.test(latest) ? latest : undefined;
8724
+ } catch {
8725
+ return undefined;
8726
+ }
8727
+ };
8728
+ try {
8729
+ const answer = spawnSync(windows ? '"' + npm + '"' : npm, args, {
8730
+ encoding: "utf8",
8731
+ shell: windows,
8732
+ windowsHide: true,
8733
+ env: { ...process.env, npm_config_cache: join(cache, "cache"), npm_config_logs_max: "0" },
8734
+ });
8735
+ const latest = answer.status === 0 ? latestOf(answer.stdout) : undefined;
8736
+ if (latest !== undefined) {
8737
+ write({ latest });
8738
+ } else {
8739
+ const said = (answer.stderr || "").trim().split(/\\r?\\n/).slice(0, 5).join("; ");
8740
+ const error = answer.error ? answer.error.message : said || "npm answered " + answer.stdout.trim();
8741
+ write({ latest: previous || undefined, error });
8742
+ }
8743
+ } finally {
8744
+ rmSync(cache, { recursive: true, force: true });
8745
+ }
8746
+ `;
8747
+ function isOff(cliName, env) {
8748
+ return Boolean(env[cliVariable(cliName, "NO_RELEASE_CHECK")] || env.CI);
8749
+ }
8750
+ function cacheFileOf(cliName, env) {
8751
+ const cache = env.XDG_CACHE_HOME || join(env.HOME || env.USERPROFILE || homedir(), ".cache");
8752
+ return join(cache, cliName, "release-check.json");
8753
+ }
8754
+ function parsedCheck(text) {
8755
+ try {
8756
+ return checkSchema.safeParse(JSON.parse(text)).data;
8757
+ } catch {
8758
+ return;
8759
+ }
8760
+ }
8761
+ async function lastCheck(file) {
8762
+ const text = await orMissing(readFile(file, "utf8"));
8763
+ return text === void 0 ? void 0 : parsedCheck(text);
8764
+ }
8765
+ async function writeCheck(file, check) {
8766
+ await mkdir(dirname(file), { recursive: true });
8767
+ const temp = tempPathOf(file);
8768
+ await writeFile(temp, JSON.stringify(check));
8769
+ await rename(temp, file);
8770
+ }
8771
+ async function startCheck(cli, install, file, last, { node }, ctx) {
8772
+ const checkedAt = ctx.clock.now();
8773
+ const previous = last?.latest ?? "";
8774
+ await writeCheck(file, {
8775
+ checkedAt,
8776
+ latest: last?.latest
8777
+ });
8778
+ const npm = globalNpm(cli, install, ctx, node);
8779
+ const { path, env } = npmOf(npm, process.platform === "win32");
8780
+ await ctx.spawnDetached(process.execPath, [
8781
+ "-e",
8782
+ CHECK_SCRIPT,
8783
+ file,
8784
+ String(checkedAt),
8785
+ previous,
8786
+ path,
8787
+ ...latestViewArgs(npm.registry)
8788
+ ], { env });
8789
+ }
8790
+ function debugLine(cli, ctx, text) {
8791
+ if (isDebug({
8792
+ env: ctx.env,
8793
+ cliName: cli.name
8794
+ })) ctx.progress(`release check failed: ${text}`);
8795
+ }
8796
+ /**
8797
+ * Tells the caller of a newer release that an earlier Release check found, and starts the next
8798
+ * check once the last is a day old.
8799
+ */
8800
+ async function releaseCheck(cli, install, runAs, launch, ctx) {
8801
+ if (launch.builtIn || launch.bin === void 0 || cli.commands === void 0) return;
8802
+ if (isOff(cli.name, ctx.env)) return;
8803
+ try {
8804
+ await checkAndTell(cli, install, runAs, launch, ctx);
8805
+ } catch (error) {
8806
+ if (errnoOf(error) === void 0 && !(error instanceof CliError)) throw error;
8807
+ debugLine(cli, ctx, messageOf(error));
8808
+ }
8809
+ }
8810
+ async function checkAndTell(cli, install, runAs, launch, ctx) {
8811
+ const file = cacheFileOf(cli.name, ctx.env);
8812
+ const last = await lastCheck(file);
8813
+ if (last?.error !== void 0) debugLine(cli, ctx, last.error);
8814
+ if (last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS) await startCheck(cli, install, file, last, launch, ctx);
8815
+ const latest = last?.latest;
8816
+ if (latest === void 0 || !isVersion(latest) || compareVersions(latest, cli.version) <= 0) return;
8817
+ ctx.notice(`${cli.name} ${latest} is out, this is ${cli.version}; run \`${runAs} update\``);
8818
+ }
8819
+ //#endregion
8675
8820
  //#region ../agent-cli/dist/built-ins/install/global.js
8676
8821
  function checkOf(cli, install, { bin, node }) {
8677
8822
  return {
@@ -8706,12 +8851,14 @@ function globalInstall(cli, install) {
8706
8851
  runAs: runAsOf(cli),
8707
8852
  builtIns: (launch) => ({ update: updateCommand(cli, install, skillOf$1(cli, adapter), launch) }),
8708
8853
  exitClasses: ["source"],
8709
- check: (launch) => checkOf(cli, install, launch)
8854
+ check: (launch) => checkOf(cli, install, launch),
8855
+ notice: (launch, ctx) => releaseCheck(cli, install, adapter.runAs, launch, ctx)
8710
8856
  };
8711
8857
  return adapter;
8712
8858
  }
8713
8859
  //#endregion
8714
8860
  //#region ../agent-cli/dist/built-ins/install/install.js
8861
+ const noNotice = async () => {};
8715
8862
  function noInstall(cli) {
8716
8863
  const skipped = async () => ({
8717
8864
  state: "skipped",
@@ -8724,7 +8871,8 @@ function noInstall(cli) {
8724
8871
  check: () => ({
8725
8872
  install: skipped,
8726
8873
  release: async () => ({ skipped: await skipped() })
8727
- })
8874
+ }),
8875
+ notice: noNotice
8728
8876
  };
8729
8877
  }
8730
8878
  function installOf(cli) {
@@ -9356,6 +9504,30 @@ function startSideTask(sideTask, runWith, { clock, failed }) {
9356
9504
  };
9357
9505
  }
9358
9506
  //#endregion
9507
+ //#region ../agent-cli/dist/built-ins/skill/usage-hint.js
9508
+ const SKILL_USAGE_CODES = /* @__PURE__ */ new Set([
9509
+ UNKNOWN_OPTION,
9510
+ UNKNOWN_COMMAND,
9511
+ INVALID_INPUT
9512
+ ]);
9513
+ /** A usage error whose hint names each Skill copy that differs from this build, since one may have named the input. */
9514
+ async function withDifferingSkill(error, cli, place, runAs) {
9515
+ if (!(error instanceof UsageError) || !SKILL_USAGE_CODES.has(error.code)) return error;
9516
+ let differing;
9517
+ try {
9518
+ differing = await skillOf$1(cli, installOf(cli)).differing(place);
9519
+ } catch (lookup) {
9520
+ if (errnoOf(lookup) === void 0) throw lookup;
9521
+ return error;
9522
+ }
9523
+ if (differing.length === 0) return error;
9524
+ return new UsageError({
9525
+ code: error.code,
9526
+ message: error.message,
9527
+ hint: `${error.hint}; the ${cli.name} skill may name what this build does not know: ${differing.join("; ")}; run \`${runAs} doctor\` to see how to update it`
9528
+ });
9529
+ }
9530
+ //#endregion
9359
9531
  //#region ../agent-cli/dist/command-line/suggest.js
9360
9532
  const MAX_DISTANCE = 3;
9361
9533
  const MIN_SIMILARITY = .4;
@@ -11066,7 +11238,11 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11066
11238
  });
11067
11239
  });
11068
11240
  } catch (error) {
11069
- return failed(error, noSideTask);
11241
+ return failed(await withDifferingSkill(error, cli, {
11242
+ cwd,
11243
+ env,
11244
+ packageRoot
11245
+ }, invocation.runAs), noSideTask);
11070
11246
  }
11071
11247
  const { input, flags } = parsed;
11072
11248
  resumed = command.deadline?.resume(input, rerunOf);
@@ -11134,9 +11310,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11134
11310
  let served = false;
11135
11311
  let settled;
11136
11312
  try {
11137
- const outcome = await control.settle(skillOf$1(cli, installOf(cli)).notice({
11313
+ const outcome = await control.settle(noticesOf(cli, {
11138
11314
  builtIn,
11139
- bin: options.bin
11315
+ bin: options.bin,
11316
+ node: options.node
11140
11317
  }, ctx).then(() => execute(command, input, ctx, {
11141
11318
  dryRun: flags.dryRun,
11142
11319
  confirm: flags.confirm,
@@ -11196,6 +11373,11 @@ function once(build) {
11196
11373
  throw outcome.error;
11197
11374
  };
11198
11375
  }
11376
+ async function noticesOf(cli, launch, ctx) {
11377
+ const install = installOf(cli);
11378
+ await skillOf$1(cli, install).notice(launch, ctx);
11379
+ await install.notice(launch, ctx);
11380
+ }
11199
11381
  //#endregion
11200
11382
  //#region ../agent-cli/dist/style.js
11201
11383
  function colourEnabled(env, stdoutIsTTY) {
@@ -11278,10 +11460,16 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
11278
11460
  ...format,
11279
11461
  json: routed.json
11280
11462
  }));
11281
- case "usage error": return ranAlone(errorResult(routed.error, {
11282
- ...misused(routed.node),
11283
- json: routed.json
11284
- }));
11463
+ case "usage error": {
11464
+ const place = {
11465
+ ...options,
11466
+ packageRoot: packageRootOf(options.bin, options.cwd)
11467
+ };
11468
+ return ranAlone(errorResult(await withDifferingSkill(routed.error, cli, place, runAs), {
11469
+ ...misused(routed.node),
11470
+ json: routed.json
11471
+ }));
11472
+ }
11285
11473
  case "command": return whileStopping(options.signal, stopNotice === void 0 ? void 0 : () => write.stderr(stopLine(stopNotice, routed.json)), () => runCommand(cli, routed, options, write, misused(routed.spec)));
11286
11474
  }
11287
11475
  }
@@ -11344,7 +11532,7 @@ async function runProcess(cli) {
11344
11532
  //#region package.json
11345
11533
  var package_default = {
11346
11534
  name: "@duyquangnvx/create-agent-cli",
11347
- version: "0.21.0",
11535
+ version: "0.23.0",
11348
11536
  description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
11349
11537
  type: "module",
11350
11538
  engines: { "node": ">=22.12.0" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duyquangnvx/create-agent-cli",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -21,7 +21,7 @@
21
21
  "access": "public"
22
22
  },
23
23
  "devDependencies": {
24
- "@duyquangnvx/agent-cli": "0.21.0",
24
+ "@duyquangnvx/agent-cli": "0.23.0",
25
25
  "tsdown": "0.23.0",
26
26
  "zod": "4.6.7"
27
27
  },
@@ -1,6 +1,6 @@
1
1
  This installs the CLI. The user has installed this skill already.
2
2
 
3
- 1. Run `node --version` and compare it with the range `npm view {{packageName}} engines.node` prints, which `doctor`'s `node` row also checks once the CLI is installed. Under nvm every Node version has its own global packages, so {{binName}} lands under the Node this shell runs. If that Node is too old, or is not the one the user's terminal runs, tell the user and let them choose before going on.
3
+ 1. Run `node --version` and compare it with the range `npm view {{packageName}} engines.node` prints, which `doctor`'s `node` row also checks once the CLI is installed. Under nvm every Node version has its own global packages, so {{binName}} lands under the Node this shell runs. If that Node is too old, or is not the one the user's terminal runs, tell the user and let them choose before going on. That is also why a {{binName}} the user installed can be missing from this shell: ask which Node their terminal runs before installing again.
4
4
  2. Hand the user the install line to type, as [Sandbox](#sandbox) says:
5
5
 
6
6
  `! npm install --global {{packageName}}@latest`
@@ -7,7 +7,7 @@ metadata:
7
7
  version: "0.0.0"
8
8
  ---
9
9
 
10
- <!-- A skeleton to rewrite around {{binName}}'s real Commands. Leave flags out: `--help` is generated from the code and stays in sync with it, this file does not. -->
10
+ <!-- A skeleton to rewrite around {{binName}}'s real Commands. A line earns its place only when no `--help`, output, hint or `doctor` row tells the agent the same thing: write down what costs it turns to work out, such as a convention, a reason, a trap, or a step that needs the user's consent. Leave flags out: `--help` is generated from the code and stays in sync with it, this file does not. What {{binName}} is for, and the nearby tasks it is not for, go in the description, which the agent always sees. -->
11
11
 
12
12
  # {{binName}}
13
13
 
@@ -19,13 +19,6 @@ metadata:
19
19
 
20
20
  The line above should show 0.0.0, the version this skill describes. If it says {{binName}} is not installed or shows another version, read [setup](references/setup.md) before anything else.
21
21
 
22
- ## When to use
23
-
24
- <!-- The tasks {{binName}} is for, and the nearby tasks it is not for. -->
25
-
26
- - Greeting someone by name.
27
- - Writing a greeting card to a file, in English for free or in another language at a cost.
28
-
29
22
  ## Workflows
30
23
 
31
24
  <!-- Each main task as steps naming the Commands it runs, in order. Move a long workflow to a file under references/ and link it. -->
@@ -43,11 +36,9 @@ The line above should show 0.0.0, the version this skill describes. If it says {
43
36
 
44
37
  To put a message under the greeting, pass `-` after the name and pipe the message in with a heredoc, so its quotes and newlines reach the card intact.
45
38
 
46
- Pass `--json` whenever you parse the output: stdout then holds only the data. A Command that changes something takes `--dry-run`, which prints its Plan, the files it writes and its cost, and changes nothing: run it first when unsure. A destructive Command also needs `--confirm=<target>`, and its error's Next step gives the exact command.
47
-
48
39
  ## Recovering from errors
49
40
 
50
- Errors go to stderr and end with a `hint:` line, then a `run:` line for each command to run next, in order. Under `--json`, every stderr line is one JSON object: `{"progress":"..."}` or `{"notice":"..."}` while it runs, then `{ errors: [{ code, message, hint, next }] }` as the last line, `next` holding those commands when there are any. Do what the hint says, except for a code below:
41
+ Do what the error's hint says, except for a code below:
51
42
 
52
43
  | Code | What to do |
53
44
  |---|---|
@@ -8,11 +8,9 @@ Work through a section in order and stop at a step that fails. Ask the user to d
8
8
 
9
9
  ## Check
10
10
 
11
- Run `{{runAs}} doctor --json`. It checks the setup without changing anything and prints `{ rows, next }`.
11
+ Run `{{runAs}} doctor --json`, which changes nothing. Hand the user each line of its `next` to type with `!`, in order, then run `doctor` again.
12
12
 
13
- - Each row has a `check`, a `state` (`ok`, `problem` or `skipped`) and a `message` saying what it found. A row that has a `fix` names the command that fixes it.
14
- - `next` lists every fix in order. Hand the user each one to type with `!`, then run `doctor` again.
15
- - {{skillCheck}} If the session does not show it, ask the user to type `/reload-skills`, and failing that to restart Claude Code.
13
+ {{skillCheck}} If the session does not show it, ask the user to type `/reload-skills`, and failing that to restart Claude Code.
16
14
 
17
15
  `{{runAs}} --version` names on its second line the `CHANGELOG.md` installed with the CLI. When the user asks what changed, read it from the top, newest first, down to the version in question.
18
16