@duyquangnvx/create-agent-cli 0.13.0 → 0.13.1

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,12 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.13.1 (2026-10-23)
8
+
9
+ ### Changed
10
+
11
+ - The hints of `INTERRUPTED`, `DEADLINE_EXCEEDED`, `DISK_FULL`, `PERMISSION_DENIED` on Windows, `PROJECT_INVALID`, `TOOL_MISSING`, the npm errors of `update` and `doctor`, and a `doctor` row's fix now name the command line to run again, in backticks like the install and login lines they name, instead of "run the command again". `INTERRUPTED` adds that a run stopped on purpose needs no rerun, and a `ctx.timeout` hint says the call may have been billed, so ask the user before running it again.
12
+
7
13
  ## v0.13.0 (2026-10-23)
8
14
 
9
15
  ### Changed
package/dist/bin.js CHANGED
@@ -6480,11 +6480,11 @@ function isCommand(node) {
6480
6480
  //#endregion
6481
6481
  //#region ../agent-cli/dist/project.js
6482
6482
  const PROJECT_INVALID = "PROJECT_INVALID";
6483
- function invalid(path, problem, options) {
6483
+ function invalid(path, problem, callerLine, options) {
6484
6484
  return new CliError({
6485
6485
  code: PROJECT_INVALID,
6486
6486
  message: `the Project file ${path} is invalid: ${problem}`,
6487
- hint: `fix ${path}, then run the command again`
6487
+ hint: `fix ${path}, then run \`${callerLine}\` again`
6488
6488
  }, options);
6489
6489
  }
6490
6490
  function readText(path, fileError) {
@@ -6498,30 +6498,30 @@ function readText(path, fileError) {
6498
6498
  });
6499
6499
  }
6500
6500
  }
6501
- function parsed(path, text, schema) {
6501
+ function parsed(path, text, schema, callerLine) {
6502
6502
  let json;
6503
6503
  try {
6504
6504
  json = JSON.parse(text);
6505
6505
  } catch (error) {
6506
- throw invalid(path, messageOf(error), { cause: error });
6506
+ throw invalid(path, messageOf(error), callerLine, { cause: error });
6507
6507
  }
6508
6508
  const result = schema.safeParse(json);
6509
- if (!result.success) throw invalid(path, prettifyError(result.error));
6509
+ if (!result.success) throw invalid(path, prettifyError(result.error), callerLine);
6510
6510
  return result.data;
6511
6511
  }
6512
- function projectFrom(projectFile, folder, fileError) {
6512
+ function projectFrom(projectFile, folder, scope) {
6513
6513
  const path = join(folder, projectFile.file);
6514
- const text = readText(path, fileError);
6514
+ const text = readText(path, scope.fileError);
6515
6515
  if (text !== void 0) return {
6516
6516
  root: folder,
6517
- settings: parsed(path, text, projectFile.schema)
6517
+ settings: parsed(path, text, projectFile.schema, scope.callerLine)
6518
6518
  };
6519
6519
  const parent = dirname(folder);
6520
- return parent === folder ? void 0 : projectFrom(projectFile, parent, fileError);
6520
+ return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
6521
6521
  }
6522
6522
  /** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
6523
- function projectOf(projectFile, cwd, fileError) {
6524
- return projectFrom(projectFile, resolve(cwd), fileError);
6523
+ function projectOf(projectFile, cwd, scope) {
6524
+ return projectFrom(projectFile, resolve(cwd), scope);
6525
6525
  }
6526
6526
  //#endregion
6527
6527
  //#region ../agent-cli/dist/tools.js
@@ -6554,7 +6554,7 @@ async function findTool(name, env) {
6554
6554
  for (const file of candidates) if (await isExecutable(file)) return file;
6555
6555
  }
6556
6556
  /** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
6557
- async function checkTools(tools, env) {
6557
+ async function checkTools(tools, env, callerLine) {
6558
6558
  const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
6559
6559
  const missing = tools.filter((_tool, index) => found[index] === void 0);
6560
6560
  if (missing.length === 0) return;
@@ -6562,7 +6562,7 @@ async function checkTools(tools, env) {
6562
6562
  throw new CliError({
6563
6563
  code: TOOL_MISSING,
6564
6564
  message: `${missing.length === 1 ? "a tool it runs is" : "tools it runs are"} not on PATH: ${names}`,
6565
- hint: `install ${missing.length === 1 ? "it" : "them"}, then run the same command again: ${missing.map(({ install }) => install).join("; ")}`
6565
+ hint: `install ${missing.map(({ name, install }) => `${name} (\`${install}\`)`).join(", ")}, then run \`${callerLine}\` again`
6566
6566
  });
6567
6567
  }
6568
6568
  function toolsIn(node) {
@@ -6795,15 +6795,15 @@ function failedError(command, { exitCode, stderr }) {
6795
6795
  hint: `the output of ${command} above says why it failed`
6796
6796
  });
6797
6797
  }
6798
- function missingError(command, cause) {
6798
+ function missingError(command, callerLine, cause) {
6799
6799
  return new CliError({
6800
6800
  code: TOOL_MISSING,
6801
6801
  message: `${command} could not be started: it is not on PATH`,
6802
- hint: `install ${command}, then run the same command again`
6802
+ hint: `install ${command}, then run \`${callerLine}\` again`
6803
6803
  }, { cause });
6804
6804
  }
6805
- function startError(command, error, fileError) {
6806
- if (isMissingFile(error)) return missingError(command, error);
6805
+ function startError(command, error, { fileError, callerLine }) {
6806
+ if (isMissingFile(error)) return missingError(command, callerLine, error);
6807
6807
  return fileError(error, {
6808
6808
  path: command,
6809
6809
  access: "read"
@@ -6833,8 +6833,7 @@ function reportingLines(onLine, onError) {
6833
6833
  }
6834
6834
  };
6835
6835
  }
6836
- /** `ctx.spawn`: `spawn` bound to the run, its start failures classified by errno. */
6837
- function contextSpawn(run, defaults, fileError) {
6836
+ function contextSpawn(run, defaults, scope) {
6838
6837
  return async (command, args = [], options = {}) => {
6839
6838
  let callerFailure;
6840
6839
  const caller = (error) => {
@@ -6850,7 +6849,7 @@ function contextSpawn(run, defaults, fileError) {
6850
6849
  stdin: reporting(options.stdin, caller)
6851
6850
  });
6852
6851
  } catch (error) {
6853
- throw callerFailure?.error === error ? error : startError(command, error, fileError);
6852
+ throw callerFailure?.error === error ? error : startError(command, error, scope);
6854
6853
  }
6855
6854
  if (options.check === true && result.exitCode !== 0) throw failedError(command, result);
6856
6855
  return result;
@@ -6927,6 +6926,7 @@ const NO_LATEST = "NO_LATEST";
6927
6926
  function npmContext(cli, registry, ctx, node, cwd) {
6928
6927
  return {
6929
6928
  cliName: cli.name,
6929
+ callerLine: ctx.commandLine(),
6930
6930
  registry,
6931
6931
  cwd,
6932
6932
  env: ctx.env,
@@ -6966,8 +6966,8 @@ function globalRegistry(install) {
6966
6966
  package: name,
6967
6967
  label: registry,
6968
6968
  flags: registryFlags(install),
6969
- unreachableHint: "check the network or VPN, then run it again",
6970
- loginHint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again`,
6969
+ unreachableHint: "check the network or VPN",
6970
+ loginHint: `log in to ${registry} (\`npm login --registry=${registry}\`) or set its token in ~/.npmrc`,
6971
6971
  missingHint: `check that ${name} is published on ${registry}`
6972
6972
  };
6973
6973
  }
@@ -7021,26 +7021,27 @@ function failureOf(call, answer, ctx) {
7021
7021
  const { label: registry, package: name, loginHint, unreachableHint } = ctx.registry;
7022
7022
  const code = NPM_CODE.exec(answer.stderr)?.[1];
7023
7023
  const output = tail$1(answer.stderr);
7024
+ const rerun = `run \`${ctx.callerLine}\` again`;
7024
7025
  if (code !== void 0 && NETWORK_CODES.has(code)) return new CliError({
7025
7026
  code: REGISTRY_UNREACHABLE,
7026
7027
  message: `npm could not reach ${registry} (${code})${output}`,
7027
- hint: unreachableHint
7028
+ hint: `${unreachableHint}, then ${rerun}`
7028
7029
  });
7029
7030
  if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
7030
7031
  code: REGISTRY_AUTH,
7031
7032
  message: `${registry} refused npm's credentials (${code})${output}`,
7032
- hint: loginHint
7033
+ hint: `${loginHint}, then ${rerun}`
7033
7034
  });
7034
7035
  if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
7035
7036
  if (call === "install") return new CliError({
7036
7037
  code: INSTALL_FAILED,
7037
7038
  message: `npm could not install ${name}${code === void 0 ? "" : ` (${code})`}${output}`,
7038
- hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.registry)}`
7039
+ hint: `fix what npm reports, then ${rerun}, or install it yourself: \`${installCommandLine(ctx.registry)}\``
7039
7040
  });
7040
7041
  return new CliError({
7041
7042
  code: NPM_FAILED,
7042
7043
  message: `npm ${call} failed with exit code ${answer.exitCode}${output}`,
7043
- hint: "fix what npm reports, then run it again"
7044
+ hint: `fix what npm reports, then ${rerun}`
7044
7045
  });
7045
7046
  }
7046
7047
  function noLatest(ctx, message) {
@@ -7328,8 +7329,8 @@ function registryOf({ package: name }, root) {
7328
7329
  package: name,
7329
7330
  label: `the registry npm uses for ${name}`,
7330
7331
  flags: [],
7331
- 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`,
7332
- loginHint: `log in to it (npm login${scope === void 0 ? "" : ` --scope=${scope}`}) or set its token in ${npmrc}, then run it again`,
7332
+ unreachableHint: scope === void 0 ? "check the network or VPN" : `check the network or VPN and the ${scope}:registry that ${npmrc} sets`,
7333
+ loginHint: `log in to it (\`npm login${scope === void 0 ? "" : ` --scope=${scope}`}\`) or set its token in ${npmrc}`,
7333
7334
  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>`
7334
7335
  };
7335
7336
  }
@@ -7475,7 +7476,10 @@ function readProject(projectFile, ctx) {
7475
7476
  try {
7476
7477
  return {
7477
7478
  ok: true,
7478
- project: projectOf(projectFile, ctx.cwd, ctx.fileError)
7479
+ project: projectOf(projectFile, ctx.cwd, {
7480
+ fileError: ctx.fileError,
7481
+ callerLine: ctx.commandLine()
7482
+ })
7479
7483
  };
7480
7484
  } catch (error) {
7481
7485
  return {
@@ -7676,11 +7680,11 @@ function render({ rows, next }, style) {
7676
7680
  return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
7677
7681
  }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
7678
7682
  }
7679
- function failure(cliName, problems) {
7683
+ function failure(cliName, callerLine, problems) {
7680
7684
  const errors = problems.map(({ check, message, fix, code }) => new CliError({
7681
7685
  code: code ?? "DOCTOR_PROBLEMS",
7682
7686
  message: `${cliName} doctor found a problem in ${check}: ${message}`,
7683
- hint: fix === void 0 ? "the row says what is wrong" : `run ${fix}, then run doctor again`
7687
+ hint: fix === void 0 ? "the row says what is wrong" : `run \`${fix}\`, then run \`${callerLine}\` again`
7684
7688
  }));
7685
7689
  return new AggregateError(errors);
7686
7690
  }
@@ -7713,7 +7717,7 @@ function doctorCommand(cli, launch) {
7713
7717
  const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
7714
7718
  if (problems.length > 0) {
7715
7719
  ctx.deliver(report);
7716
- throw failure(cli.name, problems);
7720
+ throw failure(cli.name, ctx.commandLine(), problems);
7717
7721
  }
7718
7722
  return report;
7719
7723
  },
@@ -7727,14 +7731,16 @@ const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
7727
7731
  /** How long an interrupted Command may take to settle before the run ends without it. */
7728
7732
  const GRACE_MS = 3e3;
7729
7733
  const GRACE_OVER = Symbol("grace over");
7730
- function interruptedError() {
7734
+ function interruptedError(callerLine) {
7731
7735
  return new CliError({
7732
7736
  code: INTERRUPTED,
7733
7737
  message: "interrupted before it finished",
7734
- hint: "run the command again"
7738
+ hint: `to finish it, run \`${callerLine}\` again, unless it was stopped on purpose; if a tool timeout stopped it, give it longer`
7735
7739
  });
7736
7740
  }
7737
- const WAIT_HINT = "run it again later, when the service it waits on may answer faster";
7741
+ function waitHint(callerLine, bound) {
7742
+ return bound === "timeout" ? `the call may have been billed: ask your user before running \`${callerLine}\` again` : `run \`${callerLine}\` again later, when the service it waits on may answer faster`;
7743
+ }
7738
7744
  function deadlineError(commandLine, ms, hint, bound = "deadline") {
7739
7745
  return new CliError({
7740
7746
  code: DEADLINE_EXCEEDED,
@@ -7747,8 +7753,9 @@ var RunControl = class {
7747
7753
  signal;
7748
7754
  #controller = new AbortController();
7749
7755
  #commandLine;
7756
+ #callerLine;
7750
7757
  #outer;
7751
- #onOuterAbort = () => this.#controller.abort(interruptedError());
7758
+ #onOuterAbort = () => this.#controller.abort(interruptedError(this.#callerLine));
7752
7759
  /** Each running must-finish step, tracked until it settles, with its name. */
7753
7760
  #steps = /* @__PURE__ */ new Map();
7754
7761
  #progress;
@@ -7761,8 +7768,9 @@ var RunControl = class {
7761
7768
  #graceOver = new Promise((resolve) => {
7762
7769
  this.#endGrace = resolve;
7763
7770
  });
7764
- constructor({ commandLine, outer, clock, progress, runDeadline }) {
7771
+ constructor({ commandLine, callerLine, outer, clock, progress, runDeadline }) {
7765
7772
  this.#commandLine = commandLine;
7773
+ this.#callerLine = callerLine;
7766
7774
  this.#outer = outer;
7767
7775
  this.#clock = clock;
7768
7776
  this.#progress = progress;
@@ -7784,7 +7792,7 @@ var RunControl = class {
7784
7792
  #abortsAfter(ms, bound) {
7785
7793
  const controller = new AbortController();
7786
7794
  after(this.#clock, ms, this.#disposed.signal, () => {
7787
- const reason = deadlineError(this.#commandLine, ms, WAIT_HINT, bound);
7795
+ const reason = deadlineError(this.#commandLine, ms, waitHint(this.#callerLine, bound), bound);
7788
7796
  this.#abortReasons.add(reason);
7789
7797
  controller.abort(reason);
7790
7798
  });
@@ -8098,7 +8106,7 @@ const NOT_FOUND = "NOT_FOUND";
8098
8106
  const ALREADY_EXISTS = "ALREADY_EXISTS";
8099
8107
  const NOT_A_FILE = "NOT_A_FILE";
8100
8108
  function permissionHint({ path, access, callerLine, env, platform }) {
8101
- if (platform === "win32") return `a program may hold ${path} open: close it, then run the same command again`;
8109
+ if (platform === "win32") return `a program may hold ${path} open: close it, then run \`${callerLine}\` again`;
8102
8110
  if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
8103
8111
  return `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`;
8104
8112
  }
@@ -8123,7 +8131,7 @@ function classifyErrno(error, context) {
8123
8131
  case "EDQUOT": return new CliError({
8124
8132
  code: DISK_FULL,
8125
8133
  message: `no space left on the disk holding ${path}`,
8126
- hint: "free space on that disk, then run the same command again"
8134
+ hint: `free space on that disk, then run \`${context.callerLine}\` again`
8127
8135
  }, { cause: error });
8128
8136
  case "ENOENT":
8129
8137
  case "ENOTDIR":
@@ -9046,7 +9054,7 @@ function dryRunOutcome(command, plan) {
9046
9054
  };
9047
9055
  }
9048
9056
  async function execute(command, input, ctx, { dryRun, confirm, yes, page, invocation, commandLine, started, files, guardOutputs }) {
9049
- await checkTools(command.tools ?? [], ctx.env);
9057
+ await checkTools(command.tools ?? [], ctx.env, ctx.commandLine());
9050
9058
  if (command.rows !== void 0) return outcomeOf(await command.run({
9051
9059
  ...input,
9052
9060
  ...page
@@ -9401,7 +9409,7 @@ function portsOf(cli, replacement) {
9401
9409
  return cli.ports ?? (() => ({}));
9402
9410
  }
9403
9411
  function portsBaseOf(scope, signal) {
9404
- const { cli, options, packageRoot, progress, fileError, clock, files } = scope;
9412
+ const { cli, options, packageRoot, progress, fileError, callerLine, clock, files } = scope;
9405
9413
  const { cwd, env } = options;
9406
9414
  const envFile = envFileOf(cli.name, env);
9407
9415
  return {
@@ -9426,7 +9434,10 @@ function portsBaseOf(scope, signal) {
9426
9434
  cwd,
9427
9435
  env,
9428
9436
  signal
9429
- }, fileError)
9437
+ }, {
9438
+ fileError,
9439
+ callerLine
9440
+ })
9430
9441
  };
9431
9442
  }
9432
9443
  function contextOf(portsBase, control, parts) {
@@ -9462,8 +9473,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9462
9473
  flags: knownFlags(routed.spec),
9463
9474
  stdinArg: stdinArgOf(routed)
9464
9475
  };
9476
+ const typedLine = (change) => commandLineOf(invocation, change);
9477
+ const rerunLine = typedLine();
9465
9478
  const fileError = fileErrorOf({
9466
- callerLine: commandLineOf(invocation),
9479
+ callerLine: rerunLine,
9467
9480
  env,
9468
9481
  platform
9469
9482
  });
@@ -9473,20 +9486,20 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9473
9486
  json: routed.json,
9474
9487
  invocation
9475
9488
  };
9476
- const typedLine = (change) => commandLineOf(invocation, change);
9477
- const callerLine = commandLineOf({
9489
+ const pipedLine = commandLineOf({
9478
9490
  ...invocation,
9479
9491
  stdinArg: void 0
9480
9492
  });
9481
9493
  let resumeHint;
9482
9494
  const control = new RunControl({
9483
9495
  commandLine,
9496
+ callerLine: rerunLine,
9484
9497
  outer: options.signal,
9485
9498
  clock,
9486
9499
  progress,
9487
9500
  runDeadline: command.deadline === void 0 ? void 0 : {
9488
9501
  ms: command.deadline.ms,
9489
- hint: () => resumeHint ?? stdinNeverEndedHint(callerLine)
9502
+ hint: () => resumeHint ?? stdinNeverEndedHint(pipedLine)
9490
9503
  }
9491
9504
  });
9492
9505
  const failed = (error, finishSideTask) => {
@@ -9503,7 +9516,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9503
9516
  if (isTerminal(stdin)) progress(readingLine(field.name, platform));
9504
9517
  return readStdin(stdin, control.signal, {
9505
9518
  clock,
9506
- stalled: () => stdinStalled(callerLine)
9519
+ stalled: () => stdinStalled(pipedLine)
9507
9520
  });
9508
9521
  });
9509
9522
  } catch (error) {
@@ -9521,6 +9534,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9521
9534
  packageRoot,
9522
9535
  progress,
9523
9536
  fileError,
9537
+ callerLine: rerunLine,
9524
9538
  clock,
9525
9539
  files: outputs.files
9526
9540
  };
@@ -9544,7 +9558,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9544
9558
  });
9545
9559
  let project;
9546
9560
  try {
9547
- project = builtIn || cli.project === void 0 ? void 0 : projectOf(cli.project, cwd, fileError);
9561
+ project = builtIn || cli.project === void 0 ? void 0 : projectOf(cli.project, cwd, {
9562
+ fileError,
9563
+ callerLine: rerunLine
9564
+ });
9548
9565
  } catch (error) {
9549
9566
  return failed(error, finishSideTask);
9550
9567
  }
@@ -9759,14 +9776,14 @@ async function runProcess(cli) {
9759
9776
  }
9760
9777
  //#endregion
9761
9778
  //#region package.json
9762
- var version$1 = "0.13.0";
9779
+ var version$1 = "0.13.1";
9763
9780
  var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
9764
9781
  //#endregion
9765
9782
  //#region src/build.ts
9766
9783
  const build = {
9767
- "sha": "1c3a29e",
9784
+ "sha": "7b1baa9",
9768
9785
  "dirty": false,
9769
- "builtAt": "2026-10-23T05:43:49.000Z"
9786
+ "builtAt": "2026-10-23T13:03:05.000Z"
9770
9787
  };
9771
9788
  //#endregion
9772
9789
  //#region src/steps.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duyquangnvx/create-agent-cli",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
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.13.0",
22
+ "@duyquangnvx/agent-cli": "0.13.1",
23
23
  "tsdown": "0.23.0",
24
24
  "zod": "4.6.7"
25
25
  },