@duyquangnvx/create-agent-cli 0.21.0 → 0.22.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,18 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.22.0 (2026-11-02)
8
+
9
+ ### Added
10
+
11
+ - 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.
12
+ - 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.
13
+
14
+ ### Changed
15
+
16
+ - `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`.
17
+ - 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.
18
+
7
19
  ## v0.21.0 (2026-10-29)
8
20
 
9
21
  ### Added
package/dist/bin.js CHANGED
@@ -7081,9 +7081,9 @@ function secretRows(secrets, commands) {
7081
7081
  /** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
7082
7082
  function buildStamp() {
7083
7083
  return {
7084
- "sha": "75fd8d7",
7084
+ "sha": "4664298",
7085
7085
  "dirty": false,
7086
- "builtAt": "2026-10-29T03:29:07.000Z"
7086
+ "builtAt": "2026-11-02T04:03:08.000Z"
7087
7087
  };
7088
7088
  }
7089
7089
  /** The first line of `--version`: the version, and the commit and time of a stamped build. */
@@ -7092,6 +7092,40 @@ function versionLine({ version }) {
7092
7092
  return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
7093
7093
  }
7094
7094
  //#endregion
7095
+ //#region ../agent-cli/dist/context/agent.js
7096
+ const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
7097
+ const isSet = (name) => (env) => (env[name] ?? "") !== "";
7098
+ const equals = (name, value) => (env) => env[name] === value;
7099
+ const MARKERS = [
7100
+ (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
7101
+ equals("AGENT", "amp"),
7102
+ isSet("CODEX_SANDBOX"),
7103
+ isSet("CODEX_CI"),
7104
+ isSet("CODEX_THREAD_ID"),
7105
+ isSet("GEMINI_CLI"),
7106
+ isSet("COPILOT_CLI"),
7107
+ isSet("OPENCODE"),
7108
+ isSet("ANTIGRAVITY_AGENT"),
7109
+ isSet("AUGMENT_AGENT"),
7110
+ isSet("REPL_ID"),
7111
+ isSet("CLAUDE_CODE_IS_COWORK"),
7112
+ isSet("CLAUDECODE"),
7113
+ isSet("CLAUDE_CODE"),
7114
+ isSet("CURSOR_TRACE_ID"),
7115
+ isSet("CURSOR_AGENT"),
7116
+ equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
7117
+ equals("TERM_PROGRAM", "kiro"),
7118
+ (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
7119
+ isSet("GOOSE_PROVIDER")
7120
+ ];
7121
+ /**
7122
+ * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
7123
+ * in a PTY, so a TTY alone doesn't mean a person is watching.
7124
+ */
7125
+ function isAgent(env) {
7126
+ return MARKERS.some((marker) => marker(env));
7127
+ }
7128
+ //#endregion
7095
7129
  //#region ../agent-cli/dist/built-ins/semver.js
7096
7130
  const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
7097
7131
  const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
@@ -7231,8 +7265,14 @@ function tail$1(stderr) {
7231
7265
  const lines = lastLines(stderr, STDERR_TAIL_LINES);
7232
7266
  return lines === "" ? "" : `\n${lines}`;
7233
7267
  }
7268
+ function authHint(code, ctx) {
7269
+ const { label: registry, loginHint } = ctx.registry;
7270
+ const login = `${loginHint}, then ${RUN_AGAIN}`;
7271
+ if (code !== "E403" || !isAgent(ctx.env)) return login;
7272
+ 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}`;
7273
+ }
7234
7274
  function failureOf(call, answer, ctx) {
7235
- const { label: registry, package: name, loginHint, unreachableHint } = ctx.registry;
7275
+ const { label: registry, package: name, unreachableHint } = ctx.registry;
7236
7276
  const code = NPM_CODE.exec(answer.stderr)?.[1];
7237
7277
  const output = tail$1(answer.stderr);
7238
7278
  const next = [ctx.rerun];
@@ -7245,7 +7285,7 @@ function failureOf(call, answer, ctx) {
7245
7285
  if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
7246
7286
  code: REGISTRY_AUTH,
7247
7287
  message: `${registry} refused npm's credentials (${code})${output}`,
7248
- hint: `${loginHint}, then ${RUN_AGAIN}`,
7288
+ hint: authHint(code, ctx),
7249
7289
  next
7250
7290
  });
7251
7291
  if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
@@ -7669,6 +7709,7 @@ function hubSkill(cli, from) {
7669
7709
  run: async (ctx) => findingOf(await skillCopies(cli.name, cli.version, ctx))
7670
7710
  }],
7671
7711
  notice: async () => {},
7712
+ differing: async (place) => (await skillCopies(cli.name, cli.version, place)).filter(({ state }) => state !== "current" && state !== "unreadable").map(describeCopy),
7672
7713
  nextAt: async (version, ctx) => {
7673
7714
  const copies = await skillCopies(cli.name, version, ctx);
7674
7715
  const fix = fixFor(cli.name, copies, from);
@@ -7751,40 +7792,6 @@ function agentsRow(agents, install) {
7751
7792
  };
7752
7793
  }
7753
7794
  //#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
7795
  //#region ../agent-cli/dist/context/errno.js
7789
7796
  const PERMISSION_DENIED = "PERMISSION_DENIED";
7790
7797
  const DISK_FULL = "DISK_FULL";
@@ -8305,6 +8312,15 @@ function packagedSkill(cli, { packaged: folder, agents: text }, install) {
8305
8312
  run: (ctx) => findingOf(cli, folder, install, ctx)
8306
8313
  }, ...agents === void 0 ? [] : [agentsRow(agents, install)]],
8307
8314
  notice: (launch, ctx) => notice(cli, folder, install, launch, ctx),
8315
+ differing: async ({ cwd, packageRoot }) => {
8316
+ const { copy } = await skillCopyOf(cli, cwd);
8317
+ const { copyVersion, packageVersion, same } = await skillVersionsOf(join(packageRoot, folder), copy);
8318
+ return copyVersion === void 0 || same ? [] : [compared(cli.name, {
8319
+ copy,
8320
+ copyVersion,
8321
+ packageVersion
8322
+ }, false)];
8323
+ },
8308
8324
  nextAt: async (_version, ctx) => {
8309
8325
  const { fix } = await findingOf(cli, folder, install, ctx);
8310
8326
  return fix === void 0 ? [] : [fix];
@@ -9356,6 +9372,30 @@ function startSideTask(sideTask, runWith, { clock, failed }) {
9356
9372
  };
9357
9373
  }
9358
9374
  //#endregion
9375
+ //#region ../agent-cli/dist/built-ins/skill/usage-hint.js
9376
+ const SKILL_USAGE_CODES = /* @__PURE__ */ new Set([
9377
+ UNKNOWN_OPTION,
9378
+ UNKNOWN_COMMAND,
9379
+ INVALID_INPUT
9380
+ ]);
9381
+ /** A usage error whose hint names each Skill copy that differs from this build, since one may have named the input. */
9382
+ async function withDifferingSkill(error, cli, place, runAs) {
9383
+ if (!(error instanceof UsageError) || !SKILL_USAGE_CODES.has(error.code)) return error;
9384
+ let differing;
9385
+ try {
9386
+ differing = await skillOf$1(cli, installOf(cli)).differing(place);
9387
+ } catch (lookup) {
9388
+ if (errnoOf(lookup) === void 0) throw lookup;
9389
+ return error;
9390
+ }
9391
+ if (differing.length === 0) return error;
9392
+ return new UsageError({
9393
+ code: error.code,
9394
+ message: error.message,
9395
+ 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`
9396
+ });
9397
+ }
9398
+ //#endregion
9359
9399
  //#region ../agent-cli/dist/command-line/suggest.js
9360
9400
  const MAX_DISTANCE = 3;
9361
9401
  const MIN_SIMILARITY = .4;
@@ -11066,7 +11106,11 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11066
11106
  });
11067
11107
  });
11068
11108
  } catch (error) {
11069
- return failed(error, noSideTask);
11109
+ return failed(await withDifferingSkill(error, cli, {
11110
+ cwd,
11111
+ env,
11112
+ packageRoot
11113
+ }, invocation.runAs), noSideTask);
11070
11114
  }
11071
11115
  const { input, flags } = parsed;
11072
11116
  resumed = command.deadline?.resume(input, rerunOf);
@@ -11278,10 +11322,16 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
11278
11322
  ...format,
11279
11323
  json: routed.json
11280
11324
  }));
11281
- case "usage error": return ranAlone(errorResult(routed.error, {
11282
- ...misused(routed.node),
11283
- json: routed.json
11284
- }));
11325
+ case "usage error": {
11326
+ const place = {
11327
+ ...options,
11328
+ packageRoot: packageRootOf(options.bin, options.cwd)
11329
+ };
11330
+ return ranAlone(errorResult(await withDifferingSkill(routed.error, cli, place, runAs), {
11331
+ ...misused(routed.node),
11332
+ json: routed.json
11333
+ }));
11334
+ }
11285
11335
  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
11336
  }
11287
11337
  }
@@ -11344,7 +11394,7 @@ async function runProcess(cli) {
11344
11394
  //#region package.json
11345
11395
  var package_default = {
11346
11396
  name: "@duyquangnvx/create-agent-cli",
11347
- version: "0.21.0",
11397
+ version: "0.22.0",
11348
11398
  description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
11349
11399
  type: "module",
11350
11400
  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.22.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.22.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