@duyquangnvx/create-agent-cli 0.2.1 → 0.3.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/dist/bin.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises";
3
- import { dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
2
+ import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import { access, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { spawn } from "node:child_process";
5
- import { homedir, tmpdir } from "node:os";
6
- import { parseArgs } from "node:util";
5
+ import { constants, homedir, tmpdir } from "node:os";
6
+ import { constants as constants$1, existsSync, readFileSync, realpathSync } from "node:fs";
7
+ import { parseArgs, parseEnv } from "node:util";
7
8
  import { fileURLToPath } from "node:url";
8
9
  //#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/util.js
9
10
  function getEnumValues(entries) {
@@ -761,6 +762,59 @@ function formatError(error, mapper = (issue) => issue.message) {
761
762
  processError(error);
762
763
  return fieldErrors;
763
764
  }
765
+ /** Format a ZodError as a human-readable string in the following form.
766
+ *
767
+ * From
768
+ *
769
+ * ```ts
770
+ * ZodError {
771
+ * issues: [
772
+ * {
773
+ * expected: 'string',
774
+ * code: 'invalid_type',
775
+ * path: [ 'username' ],
776
+ * message: 'Invalid input: expected string'
777
+ * },
778
+ * {
779
+ * expected: 'number',
780
+ * code: 'invalid_type',
781
+ * path: [ 'favoriteNumbers', 1 ],
782
+ * message: 'Invalid input: expected number'
783
+ * }
784
+ * ];
785
+ * }
786
+ * ```
787
+ *
788
+ * to
789
+ *
790
+ * ```
791
+ * username
792
+ * ✖ Expected number, received string at "username
793
+ * favoriteNumbers[0]
794
+ * ✖ Invalid input: expected number
795
+ * ```
796
+ */
797
+ function toDotPath(_path) {
798
+ const segs = [];
799
+ const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
800
+ for (const seg of path) if (typeof seg === "number") segs.push(`[${seg}]`);
801
+ else if (typeof seg === "symbol") segs.push(`[${JSON.stringify(String(seg))}]`);
802
+ else if (/[^\w$]/.test(seg)) segs.push(`[${JSON.stringify(seg)}]`);
803
+ else {
804
+ if (segs.length) segs.push(".");
805
+ segs.push(seg);
806
+ }
807
+ return segs.join("");
808
+ }
809
+ function prettifyError(error) {
810
+ const lines = [];
811
+ const issues = [...error.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);
812
+ for (const issue of issues) {
813
+ lines.push(`✖ ${issue.message}`);
814
+ if (issue.path?.length) lines.push(` → at ${toDotPath(issue.path)}`);
815
+ }
816
+ return lines.join("\n");
817
+ }
764
818
  //#endregion
765
819
  //#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/parse.js
766
820
  function finalizeParams(callee, params) {
@@ -5515,6 +5569,18 @@ function superRefine(fn, params) {
5515
5569
  return /* @__PURE__ */ _superRefine(fn, params);
5516
5570
  }
5517
5571
  //#endregion
5572
+ //#region ../agent-cli/dist/abort.js
5573
+ /** Never resolves; rejects with the signal's reason once it aborts. */
5574
+ function untilAborted(signal) {
5575
+ return new Promise((_resolve, reject) => {
5576
+ if (signal.aborted) {
5577
+ reject(signal.reason);
5578
+ return;
5579
+ }
5580
+ signal.addEventListener("abort", () => reject(signal.reason), { once: true });
5581
+ });
5582
+ }
5583
+ //#endregion
5518
5584
  //#region ../agent-cli/dist/input.js
5519
5585
  function isWrapper(schema) {
5520
5586
  return schema instanceof ZodOptional || schema instanceof ZodNullable || schema instanceof ZodDefault || schema instanceof ZodPrefault;
@@ -5571,42 +5637,44 @@ function optionField(key, schema) {
5571
5637
  usage
5572
5638
  };
5573
5639
  }
5574
- function fieldsOf(schema, toField) {
5640
+ function fieldsOf$1(schema, toField) {
5575
5641
  return schema === void 0 ? [] : Object.entries(schema.shape).map(([key, value]) => toField(key, value));
5576
5642
  }
5577
5643
  function argFields(schema) {
5578
- return fieldsOf(schema, argField);
5644
+ return fieldsOf$1(schema, argField);
5579
5645
  }
5580
5646
  function optionFields(schema) {
5581
- return fieldsOf(schema, optionField);
5647
+ return fieldsOf$1(schema, optionField);
5582
5648
  }
5583
5649
  //#endregion
5584
5650
  //#region ../agent-cli/dist/errors.js
5585
5651
  var CliError = class extends Error {
5586
5652
  code;
5587
5653
  hint;
5588
- retryable;
5589
- constructor({ code, message, hint, retryable }, options) {
5654
+ details;
5655
+ retryAt;
5656
+ constructor({ code, message, hint, details, retryAt }, options) {
5590
5657
  super(message, options);
5591
5658
  this.name = "CliError";
5592
5659
  this.code = code;
5593
5660
  this.hint = hint;
5594
- this.retryable = retryable;
5661
+ this.details = details;
5662
+ this.retryAt = retryAt;
5595
5663
  }
5596
5664
  };
5597
- /** The caller called the Agent CLI wrong; retrying the same call can't succeed. */
5665
+ /** The caller called the Agent CLI wrong; the same call can't succeed. */
5598
5666
  var UsageError = class extends CliError {
5599
- constructor(details) {
5600
- super({
5601
- ...details,
5602
- retryable: false
5603
- });
5667
+ constructor(fields) {
5668
+ super(fields);
5604
5669
  this.name = "UsageError";
5605
5670
  }
5606
5671
  };
5607
5672
  const INTERNAL_CODE = "INTERNAL";
5673
+ function errnoOf(error) {
5674
+ return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
5675
+ }
5608
5676
  function isMissingFile(error) {
5609
- return error instanceof Error && "code" in error && error.code === "ENOENT";
5677
+ return errnoOf(error) === "ENOENT";
5610
5678
  }
5611
5679
  function debugVariable(cliName) {
5612
5680
  return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_DEBUG`;
@@ -5617,47 +5685,61 @@ function messageOf(error) {
5617
5685
  function isDebug({ env, cliName }) {
5618
5686
  return Boolean(env[debugVariable(cliName)]);
5619
5687
  }
5620
- function detailsOf(error, format) {
5688
+ function bugHint(format) {
5689
+ return isDebug(format) ? "the stack trace shows where it failed" : `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
5690
+ }
5691
+ function fieldsOf(error, format) {
5621
5692
  if (error instanceof CliError) {
5622
- const { code, message, hint, retryable } = error;
5693
+ const { code, message, hint, details, retryAt } = error;
5623
5694
  return {
5624
5695
  code,
5625
5696
  message,
5626
- hint,
5627
- retryable
5697
+ hint: format.isBug(error) ? bugHint(format) : hint,
5698
+ details,
5699
+ retryAt
5628
5700
  };
5629
5701
  }
5630
5702
  return {
5631
5703
  code: INTERNAL_CODE,
5632
5704
  message: messageOf(error),
5633
- hint: isDebug(format) ? "the stack trace shows where it failed" : `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`,
5634
- retryable: false
5705
+ hint: bugHint(format)
5635
5706
  };
5636
5707
  }
5637
- function stackOf(error, format) {
5638
- return isDebug(format) && !(error instanceof CliError) && error instanceof Error ? error.stack : void 0;
5708
+ function causesOf(error) {
5709
+ return error instanceof Error && error.cause !== void 0 ? [error.cause, ...causesOf(error.cause)] : [];
5639
5710
  }
5640
- function formatRuntimeError(error, format) {
5641
- const details = detailsOf(error, format);
5642
- const stack = stackOf(error, format);
5643
- if (format.json) return `${JSON.stringify({
5644
- ...details,
5645
- stack
5646
- })}\n`;
5711
+ function traceOf(error) {
5712
+ return error instanceof Error ? error.stack ?? error.message : String(error);
5713
+ }
5714
+ /** Under debug: a bug's stack trace, then every error's cause chain. */
5715
+ function debugTrace(error, format) {
5716
+ if (!isDebug(format)) return void 0;
5717
+ const lines = [...format.isBug(error) ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
5718
+ return lines.length === 0 ? void 0 : lines.join("\n");
5719
+ }
5720
+ /** The errors a run failed with: each one an `AggregateError` holds, or the one it threw. */
5721
+ function errorsOf(error) {
5722
+ return error instanceof AggregateError && error.errors.length > 0 ? error.errors : [error];
5723
+ }
5724
+ function errorLines(error, format) {
5725
+ const fields = fieldsOf(error, format);
5726
+ const stack = debugTrace(error, format);
5647
5727
  const { style } = format;
5648
- const label = style.red(error instanceof CliError ? "error:" : "internal error:");
5728
+ const label = style.red(format.isBug(error) ? "internal error:" : "error:");
5649
5729
  const trace = stack === void 0 ? "" : `${stack}\n`;
5650
- return `${label} ${details.message}\n${trace}${style.cyan("hint:")} ${details.hint}\n`;
5730
+ return `${label} ${fields.message}\n${trace}${style.cyan("hint:")} ${fields.hint}\n`;
5651
5731
  }
5652
- /** A failure that still prints the Command's data on stdout, as `doctor` does with its rows. */
5653
- var FailureWithData = class extends CliError {
5654
- data;
5655
- constructor(details, data) {
5656
- super(details);
5657
- this.name = "FailureWithData";
5658
- this.data = data;
5732
+ function formatRuntimeError(error, format) {
5733
+ const errors = errorsOf(error);
5734
+ if (format.json) {
5735
+ const printed = errors.map((each) => ({
5736
+ ...fieldsOf(each, format),
5737
+ stack: debugTrace(each, format)
5738
+ }));
5739
+ return `${JSON.stringify({ errors: printed })}\n`;
5659
5740
  }
5660
- };
5741
+ return errors.map((each) => errorLines(each, format)).join("");
5742
+ }
5661
5743
  const SHELL_SAFE = /^[\w@%+=:,./-]+$/;
5662
5744
  function quoted$1(token) {
5663
5745
  return SHELL_SAFE.test(token) ? token : `'${token.replaceAll("'", `'\\''`)}'`;
@@ -5665,6 +5747,10 @@ function quoted$1(token) {
5665
5747
  function pasteable(cliName, tokens) {
5666
5748
  return [cliName, ...tokens].map(quoted$1).join(" ");
5667
5749
  }
5750
+ /** The caller's command line as typed, ready to paste. */
5751
+ function commandLineOf({ cliName, argv }) {
5752
+ return pasteable(cliName, argv);
5753
+ }
5668
5754
  function split(argv) {
5669
5755
  const end = argv.indexOf("--");
5670
5756
  return end === -1 ? {
@@ -5678,13 +5764,16 @@ function split(argv) {
5678
5764
  function without(flag, tokens) {
5679
5765
  return tokens.filter((token, index) => token !== flag && !token.startsWith(`${flag}=`) && tokens[index - 1] !== flag);
5680
5766
  }
5681
- /** The caller's command line, ready to paste, with `flag value` replacing any value they gave. */
5767
+ /**
5768
+ * The caller's command line, ready to paste, with `flag value` (or `flag` alone, for a flag that
5769
+ * takes nothing) replacing any value they gave.
5770
+ */
5682
5771
  function commandLineWith({ cliName, argv }, flag, value) {
5683
5772
  const { options, operands } = split(argv);
5773
+ const added = value === void 0 ? [flag] : [flag, value];
5684
5774
  return pasteable(cliName, [
5685
5775
  ...without(flag, options),
5686
- flag,
5687
- value,
5776
+ ...added,
5688
5777
  ...operands
5689
5778
  ]);
5690
5779
  }
@@ -5700,7 +5789,45 @@ function commandLineRenaming({ cliName, argv }, renames) {
5700
5789
  return pasteable(cliName, [...options.map((token) => renamed(token, renames)), ...operands]);
5701
5790
  }
5702
5791
  //#endregion
5792
+ //#region ../agent-cli/dist/usage.js
5793
+ const INVALID_INPUT = "INVALID_INPUT";
5794
+ const UNKNOWN_OPTION = "UNKNOWN_OPTION";
5795
+ const UNKNOWN_COMMAND = "UNKNOWN_COMMAND";
5796
+ function unknownError({ code, problem, commandLine, corrections, listed }) {
5797
+ return new UsageError({
5798
+ code,
5799
+ message: `${problem} for \`${commandLine}\``,
5800
+ hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
5801
+ });
5802
+ }
5803
+ function unknownOptionError(commandLine, invocation, flag, suggestions) {
5804
+ return unknownError({
5805
+ code: UNKNOWN_OPTION,
5806
+ problem: `unknown option '${flag}'`,
5807
+ commandLine,
5808
+ corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
5809
+ listed: "options"
5810
+ });
5811
+ }
5812
+ function unknownCommandError(commandLine, name, suggestions) {
5813
+ return unknownError({
5814
+ code: UNKNOWN_COMMAND,
5815
+ problem: `unknown command '${name}'`,
5816
+ commandLine,
5817
+ corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
5818
+ listed: "commands"
5819
+ });
5820
+ }
5821
+ function invalidInputError(commandLine, problem) {
5822
+ return new UsageError({
5823
+ code: INVALID_INPUT,
5824
+ message: problem,
5825
+ hint: `run \`${commandLine} --help\` to see the expected input`
5826
+ });
5827
+ }
5828
+ //#endregion
5703
5829
  //#region ../agent-cli/dist/list.js
5830
+ const UNKNOWN_FIELD = "UNKNOWN_FIELD";
5704
5831
  const DEFAULT_PAGE_LIMIT = 20;
5705
5832
  const FIELDS_FLAG = "--fields";
5706
5833
  const SEPARATOR = ",";
@@ -5770,13 +5897,13 @@ function selectedFields(values, rowFields, commandLine) {
5770
5897
  const hint = `pass ${FIELDS_FLAG} with any of: ${rowFields.join(", ")}`;
5771
5898
  const names = typeof raw === "string" ? raw.split(SEPARATOR).filter((name) => name !== "") : [];
5772
5899
  if (names.length === 0) throw new UsageError({
5773
- code: "INVALID_INPUT",
5900
+ code: INVALID_INPUT,
5774
5901
  message: `${FIELDS_FLAG}: missing value for \`${commandLine}\``,
5775
5902
  hint
5776
5903
  });
5777
5904
  const unknown = names.filter((name) => !rowFields.includes(name));
5778
5905
  if (unknown.length > 0) throw new UsageError({
5779
- code: "UNKNOWN_FIELD",
5906
+ code: UNKNOWN_FIELD,
5780
5907
  message: `unknown field ${quoted(unknown)} for \`${commandLine}\``,
5781
5908
  hint
5782
5909
  });
@@ -5842,6 +5969,16 @@ const CONFIRM_FLAG = standardFlag("--confirm", {
5842
5969
  takes: "value",
5843
5970
  schema: string()
5844
5971
  });
5972
+ const YES_FLAG = standardFlag("--yes", {
5973
+ description: "approve the cost the run states; --dry-run shows it",
5974
+ takes: "nothing",
5975
+ schema: boolean()
5976
+ });
5977
+ const FORCE_FLAG = standardFlag("--force", {
5978
+ description: "replace the files the run writes when they exist; --dry-run lists them",
5979
+ takes: "nothing",
5980
+ schema: boolean()
5981
+ });
5845
5982
  const EFFECT_FLAGS = {
5846
5983
  read: [JSON_FLAG],
5847
5984
  write: [JSON_FLAG, DRY_RUN_FLAG],
@@ -5849,16 +5986,24 @@ const EFFECT_FLAGS = {
5849
5986
  JSON_FLAG,
5850
5987
  DRY_RUN_FLAG,
5851
5988
  CONFIRM_FLAG
5852
- ]
5989
+ ],
5990
+ serve: [JSON_FLAG]
5853
5991
  };
5854
5992
  const RESERVED = new Map([
5855
5993
  ...ROUTING_FLAGS,
5856
5994
  ...EFFECT_FLAGS.destructive.map(({ flag }) => flag),
5995
+ YES_FLAG.flag,
5996
+ FORCE_FLAG.flag,
5857
5997
  ...LIST_FLAGS
5858
5998
  ].map((flag) => [nameOf(flag), flag]));
5859
- /** The Standard flags of a Command with this Effect, and of a List Command. */
5860
- function standardFlagsFor(effect, list) {
5861
- return [...EFFECT_FLAGS[effect], ...list?.standardFlags ?? []];
5999
+ /** The Standard flags of a Command with this Effect and declarations, and of a List Command. */
6000
+ function standardFlagsFor(effect, { outputs, cost }, list) {
6001
+ return [
6002
+ ...EFFECT_FLAGS[effect],
6003
+ ...outputs ? [FORCE_FLAG] : [],
6004
+ ...cost ? [YES_FLAG] : [],
6005
+ ...list?.standardFlags ?? []
6006
+ ];
5862
6007
  }
5863
6008
  /** Throws when the Command declares an arg or option under a Standard flag's name. */
5864
6009
  function checkNoStandardFlags(command) {
@@ -5876,10 +6021,14 @@ function checkNoStandardFlags(command) {
5876
6021
  }
5877
6022
  //#endregion
5878
6023
  //#region ../agent-cli/dist/define.js
5879
- function defineCommand(command) {
6024
+ function checkedCommand(command) {
5880
6025
  checkNoStandardFlags(command);
5881
6026
  return command;
5882
6027
  }
6028
+ function defineCommandFor() {
6029
+ return checkedCommand;
6030
+ }
6031
+ const defineCommand$1 = defineCommandFor();
5883
6032
  function isCommand(node) {
5884
6033
  return typeof node.effect === "string";
5885
6034
  }
@@ -5945,7 +6094,18 @@ function meetsMinimum(version, minimum) {
5945
6094
  return true;
5946
6095
  }
5947
6096
  //#endregion
6097
+ //#region ../agent-cli/dist/tail.js
6098
+ /** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
6099
+ function lastLines(output, count) {
6100
+ return output.trim().split("\n").slice(-count).join("\n");
6101
+ }
6102
+ //#endregion
5948
6103
  //#region ../agent-cli/dist/npm.js
6104
+ const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
6105
+ const REGISTRY_AUTH = "REGISTRY_AUTH";
6106
+ const INSTALL_FAILED = "INSTALL_FAILED";
6107
+ const NPM_FAILED = "NPM_FAILED";
6108
+ const NO_LATEST = "NO_LATEST";
5949
6109
  function npmContext(cli, install, ctx) {
5950
6110
  return {
5951
6111
  cliName: cli.name,
@@ -5955,6 +6115,7 @@ function npmContext(cli, install, ctx) {
5955
6115
  };
5956
6116
  }
5957
6117
  const NPMJS = "https://registry.npmjs.org/";
6118
+ const STDERR_TAIL_LINES$1 = 10;
5958
6119
  const RM_RETRIES = 3;
5959
6120
  const NETWORK_CODES = /* @__PURE__ */ new Set([
5960
6121
  "ENOTFOUND",
@@ -5986,7 +6147,7 @@ function installCommandLine(install, version = "latest") {
5986
6147
  }
5987
6148
  function spawnNpm(args, ctx, signal) {
5988
6149
  const windows = process.platform === "win32";
5989
- const command = windows ? ["npm", ...args].map((arg) => `"${arg.replaceAll("\"", "\"\"")}"`).join(" ") : "npm";
6150
+ const command = windows ? ["npm", ...args.map((arg) => `"${arg.replaceAll("\"", "\"\"")}"`)].join(" ") : "npm";
5990
6151
  return new Promise((resolve, reject) => {
5991
6152
  const child = spawn(command, windows ? [] : args, {
5992
6153
  env: ctx.env,
@@ -6017,45 +6178,40 @@ function spawnNpm(args, ctx, signal) {
6017
6178
  });
6018
6179
  }
6019
6180
  function tail$1(stderr) {
6020
- const lines = stderr.trim().split("\n").slice(-10);
6021
- return lines.length === 0 || lines[0] === "" ? "" : `\n${lines.join("\n")}`;
6181
+ const lines = lastLines(stderr, STDERR_TAIL_LINES$1);
6182
+ return lines === "" ? "" : `\n${lines}`;
6022
6183
  }
6023
6184
  function failureOf(call, answer, ctx) {
6024
6185
  const { registry, package: name } = ctx.install;
6025
6186
  const code = NPM_CODE.exec(answer.stderr)?.[1];
6026
6187
  const output = tail$1(answer.stderr);
6027
6188
  if (code !== void 0 && NETWORK_CODES.has(code)) return new CliError({
6028
- code: "REGISTRY_UNREACHABLE",
6189
+ code: REGISTRY_UNREACHABLE,
6029
6190
  message: `npm could not reach ${registry} (${code})${output}`,
6030
- hint: "check the network or VPN, then run it again",
6031
- retryable: true
6191
+ hint: "check the network or VPN, then run it again"
6032
6192
  });
6033
6193
  if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
6034
- code: "REGISTRY_AUTH",
6194
+ code: REGISTRY_AUTH,
6035
6195
  message: `${registry} refused npm's credentials (${code})${output}`,
6036
- hint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again`,
6037
- retryable: false
6196
+ hint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again`
6038
6197
  });
6039
6198
  if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
6040
6199
  if (call === "install") return new CliError({
6041
- code: "INSTALL_FAILED",
6200
+ code: INSTALL_FAILED,
6042
6201
  message: `npm could not install ${name}${code === void 0 ? "" : ` (${code})`}${output}`,
6043
- hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.install)}`,
6044
- retryable: false
6202
+ hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.install)}`
6045
6203
  });
6046
6204
  return new CliError({
6047
- code: "NPM_FAILED",
6205
+ code: NPM_FAILED,
6048
6206
  message: `npm ${call} failed with exit code ${answer.exitCode}${output}`,
6049
- hint: "fix what npm reports, then run it again",
6050
- retryable: false
6207
+ hint: "fix what npm reports, then run it again"
6051
6208
  });
6052
6209
  }
6053
6210
  function noLatest(ctx, message) {
6054
6211
  return new CliError({
6055
- code: "NO_LATEST",
6212
+ code: NO_LATEST,
6056
6213
  message,
6057
- hint: `check that ${ctx.install.package} is published on ${ctx.install.registry}`,
6058
- retryable: false
6214
+ hint: `check that ${ctx.install.package} is published on ${ctx.install.registry}`
6059
6215
  });
6060
6216
  }
6061
6217
  /**
@@ -6072,10 +6228,9 @@ async function runNpm(call, args, ctx, signal = ctx.signal) {
6072
6228
  ], ctx, signal).catch((error) => {
6073
6229
  if (signal?.aborted === true) throw signal.reason;
6074
6230
  throw new CliError({
6075
- code: "NPM_FAILED",
6231
+ code: NPM_FAILED,
6076
6232
  message: `npm could not be started: ${messageOf(error)}`,
6077
- hint: "install Node with npm, or put npm on PATH",
6078
- retryable: false
6233
+ hint: "install Node with npm, or put npm on PATH"
6079
6234
  }, { cause: error });
6080
6235
  });
6081
6236
  if (answer.exitCode !== 0) throw failureOf(call, answer, ctx);
@@ -6126,16 +6281,16 @@ async function latestVersion(ctx) {
6126
6281
  }
6127
6282
  //#endregion
6128
6283
  //#region ../agent-cli/dist/global-install.js
6284
+ const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
6129
6285
  /** Where npm puts global packages under its global prefix. */
6130
6286
  function globalModulesDir(prefix) {
6131
6287
  return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
6132
6288
  }
6133
6289
  function notGlobal(ctx, message) {
6134
6290
  return new CliError({
6135
- code: "NOT_GLOBAL_INSTALL",
6291
+ code: NOT_GLOBAL_INSTALL,
6136
6292
  message,
6137
- hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.install)}`,
6138
- retryable: false
6293
+ hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.install)}`
6139
6294
  });
6140
6295
  }
6141
6296
  async function realpathOrUndefined(path) {
@@ -6166,131 +6321,941 @@ async function globalInstallOf(bin, ctx) {
6166
6321
  };
6167
6322
  }
6168
6323
  //#endregion
6169
- //#region ../agent-cli/dist/skills.js
6170
- /** Where the skills CLI (`npx skills add`) puts a skill, under a project and under home. */
6171
- const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
6172
- function skillUpdateCommand(cliName) {
6173
- return `npx -y skills update ${cliName}`;
6324
+ //#region ../agent-cli/dist/confirm.js
6325
+ const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
6326
+ const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
6327
+ function checkConfirmation({ invocation, commandLine, given }, expected) {
6328
+ if (given === expected) return;
6329
+ const hint = `to confirm, run \`${commandLineWith(invocation, CONFIRM_FLAG.flag, expected)}\``;
6330
+ if (given === void 0) throw new UsageError({
6331
+ code: CONFIRMATION_REQUIRED,
6332
+ message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
6333
+ hint
6334
+ });
6335
+ throw new UsageError({
6336
+ code: CONFIRMATION_MISMATCH,
6337
+ message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
6338
+ hint
6339
+ });
6174
6340
  }
6175
- const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
6176
- const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
6177
- /** The version a SKILL.md's frontmatter `metadata.version` states. */
6178
- function skillVersionIn(skill) {
6179
- const frontmatter = FRONTMATTER.exec(skill)?.[1];
6180
- return frontmatter === void 0 ? void 0 : METADATA_VERSION.exec(`${frontmatter}\n`)?.[1];
6341
+ //#endregion
6342
+ //#region ../agent-cli/dist/cost.js
6343
+ const NOT_CONFIRMED = "NOT_CONFIRMED";
6344
+ /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
6345
+ function checkCost({ invocation, commandLine, yes }, cost) {
6346
+ if (cost === void 0 || yes) return;
6347
+ throw new CliError({
6348
+ code: NOT_CONFIRMED,
6349
+ message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
6350
+ hint: `to approve the cost, run \`${commandLineWith(invocation, YES_FLAG.flag)}\``
6351
+ });
6181
6352
  }
6182
- function homeOf(env) {
6183
- return env.HOME ?? env.USERPROFILE ?? homedir();
6353
+ //#endregion
6354
+ //#region ../agent-cli/dist/agent.js
6355
+ const AGENT_VARIABLES = [
6356
+ "CLAUDECODE",
6357
+ "CODEX_CI",
6358
+ "CODEX_SANDBOX",
6359
+ "GEMINI_CLI",
6360
+ "CURSOR_AGENT",
6361
+ "CI"
6362
+ ];
6363
+ /** Whether a Coding agent, not a person, is running the Agent CLI. */
6364
+ function isAgent(env) {
6365
+ return AGENT_VARIABLES.some((name) => env[name] !== void 0);
6184
6366
  }
6185
- async function copyAt(path, cliVersion) {
6186
- let skill;
6187
- try {
6188
- skill = await readFile(join(path, "SKILL.md"), "utf8");
6189
- } catch (error) {
6190
- if (isMissingFile(error)) return;
6191
- return {
6192
- path,
6193
- version: void 0,
6194
- state: "unreadable"
6195
- };
6367
+ //#endregion
6368
+ //#region ../agent-cli/dist/errno.js
6369
+ const PERMISSION_DENIED = "PERMISSION_DENIED";
6370
+ const DISK_FULL = "DISK_FULL";
6371
+ const NOT_FOUND = "NOT_FOUND";
6372
+ function permissionHint({ path, callerLine, env }) {
6373
+ return isAgent(env) ? `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or to allow ${path} in the sandbox's write list` : `check the permissions of ${path} and its folder`;
6374
+ }
6375
+ /** The error a file system failure on `path` stands for; any other error comes back unchanged. */
6376
+ function classifyErrno(error, context) {
6377
+ const { path } = context;
6378
+ switch (errnoOf(error)) {
6379
+ case "EACCES":
6380
+ case "EPERM": return new CliError({
6381
+ code: PERMISSION_DENIED,
6382
+ message: `permission denied: ${path}`,
6383
+ hint: permissionHint(context)
6384
+ }, { cause: error });
6385
+ case "ENOSPC": return new CliError({
6386
+ code: DISK_FULL,
6387
+ message: `no space left on the disk holding ${path}`,
6388
+ hint: "free space on that disk, then run the same command again"
6389
+ }, { cause: error });
6390
+ case "ENOENT": return new CliError({
6391
+ code: NOT_FOUND,
6392
+ message: `not found: ${path}`,
6393
+ hint: `check that ${path} and its folder exist`
6394
+ }, { cause: error });
6395
+ default: return error;
6196
6396
  }
6197
- const version = skillVersionIn(skill);
6198
- if (version === void 0 || !isVersion(version) || !isVersion(cliVersion)) return {
6199
- path,
6200
- version,
6201
- state: "unversioned"
6202
- };
6203
- return {
6204
- path,
6205
- version,
6206
- state: standingOf(version, cliVersion)
6207
- };
6208
6397
  }
6209
- /** Every installed copy of the skill named after the CLI, in the project first, then under home. */
6210
- async function skillCopies(skillName, cliVersion, cwd, env) {
6211
- const paths = [cwd, homeOf(env)].flatMap((root) => SKILL_DIRS.map((dir) => join(root, ...dir, skillName)));
6212
- const seen = /* @__PURE__ */ new Set();
6213
- const copies = [];
6214
- for (const path of paths) {
6215
- const copy = await copyAt(path, cliVersion);
6216
- const real = copy === void 0 ? void 0 : await realpath(path);
6217
- if (copy !== void 0 && real !== void 0 && !seen.has(real)) {
6218
- seen.add(real);
6219
- copies.push(copy);
6398
+ //#endregion
6399
+ //#region ../agent-cli/dist/clock.js
6400
+ const realClock = {
6401
+ now: () => Date.now(),
6402
+ sleep: (ms, signal) => new Promise((resolve, reject) => {
6403
+ if (signal?.aborted === true) {
6404
+ reject(signal.reason);
6405
+ return;
6220
6406
  }
6407
+ const onAbort = () => {
6408
+ clearTimeout(timer);
6409
+ reject(signal?.reason);
6410
+ };
6411
+ const timer = setTimeout(() => {
6412
+ signal?.removeEventListener("abort", onAbort);
6413
+ resolve();
6414
+ }, ms);
6415
+ signal?.addEventListener("abort", onAbort, { once: true });
6416
+ })
6417
+ };
6418
+ /** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
6419
+ function after(clock, ms, cancel, then) {
6420
+ clock.sleep(ms, cancel).then(then, (error) => {
6421
+ if (!cancel.aborted) throw error;
6422
+ });
6423
+ }
6424
+ async function poll(clock, check, intervalMs, signal) {
6425
+ for (;;) {
6426
+ signal.throwIfAborted();
6427
+ const value = await check();
6428
+ if (value !== void 0) return value;
6429
+ await clock.sleep(intervalMs, signal);
6221
6430
  }
6222
- return copies;
6223
6431
  }
6224
6432
  //#endregion
6225
- //#region ../agent-cli/dist/doctor-rows.js
6226
- function ok(message) {
6227
- return {
6228
- state: "ok",
6229
- message
6230
- };
6231
- }
6232
- function nodeRow(cli) {
6233
- return {
6234
- name: "node",
6235
- run: () => {
6236
- const found = process.versions.node;
6237
- const range = cli.engines?.node;
6238
- if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
6239
- const minimum = engineMinimum(range);
6240
- if (minimum === void 0) return {
6241
- state: "problem",
6242
- message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
6243
- };
6244
- if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
6245
- const major = minimum[0];
6246
- return {
6247
- state: "problem",
6248
- message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
6249
- fix: `nvm install ${major} && nvm alias default ${major}`
6250
- };
6251
- }
6252
- };
6433
+ //#region ../agent-cli/dist/interrupts.js
6434
+ const INTERRUPTED = "INTERRUPTED";
6435
+ const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
6436
+ /** How long an interrupted Command may take to settle before the run ends without it. */
6437
+ const GRACE_MS = 3e3;
6438
+ const GRACE_OVER = Symbol("grace over");
6439
+ function interruptedError() {
6440
+ return new CliError({
6441
+ code: INTERRUPTED,
6442
+ message: "interrupted before it finished",
6443
+ hint: "run the command again"
6444
+ });
6253
6445
  }
6254
- function installRow(cli, bin) {
6255
- return {
6256
- name: "install",
6257
- run: async (ctx) => {
6258
- const { install } = cli;
6259
- if (install === void 0) return ok(`${cli.name} declares no install, so it has no \`update\``);
6260
- try {
6261
- return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx))).prefix}`);
6262
- } catch (error) {
6263
- if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
6264
- state: "problem",
6265
- message: error.message,
6266
- fix: installCommandLine(install)
6267
- };
6268
- throw error;
6269
- }
6270
- }
6271
- };
6446
+ const WAIT_HINT = "run it again later, when the service it waits on may answer faster";
6447
+ function deadlineError(commandLine, ms, hint) {
6448
+ return new CliError({
6449
+ code: DEADLINE_EXCEEDED,
6450
+ message: `${commandLine} ran past its ${ms} ms deadline`,
6451
+ hint
6452
+ });
6272
6453
  }
6273
- function registryRow(cli) {
6274
- return {
6275
- name: "registry",
6276
- run: async (ctx) => {
6277
- const { install } = cli;
6278
- if (install === void 0) return ok(`${cli.name} declares no registry to compare this build with`);
6279
- const latest = await latestVersion(npmContext(cli, install, ctx));
6280
- const standing = standingOf(cli.version, latest);
6281
- const head = `${install.registry} has ${latest} as latest: this build`;
6282
- if (standing === "behind") return {
6283
- state: "problem",
6284
- message: `${head} (${cli.version}) is older`,
6285
- fix: `${cli.name} update`
6286
- };
6287
- return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
6454
+ /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
6455
+ var RunControl = class {
6456
+ signal;
6457
+ #controller = new AbortController();
6458
+ #commandLine;
6459
+ #outer;
6460
+ #onOuterAbort = () => this.#controller.abort(interruptedError());
6461
+ /** Each running must-finish step, tracked until it settles, with its name. */
6462
+ #steps = /* @__PURE__ */ new Map();
6463
+ #progress;
6464
+ #clock;
6465
+ #disposed = new AbortController();
6466
+ #deadlineHit;
6467
+ #graceCancel = new AbortController();
6468
+ #endGrace = () => {};
6469
+ #graceOver = new Promise((resolve) => {
6470
+ this.#endGrace = resolve;
6471
+ });
6472
+ constructor({ commandLine, outer, clock, progress, runDeadline }) {
6473
+ this.#commandLine = commandLine;
6474
+ this.#outer = outer;
6475
+ this.#clock = clock;
6476
+ this.#progress = progress;
6477
+ this.signal = this.#controller.signal;
6478
+ this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
6479
+ if (outer?.aborted === true) this.#onOuterAbort();
6480
+ else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
6481
+ if (runDeadline !== void 0) {
6482
+ const { ms, hint } = runDeadline;
6483
+ after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, hint)));
6288
6484
  }
6289
- };
6290
- }
6291
- const SKILL_STATE_NOTES = {
6292
- current: "the same as this build",
6293
- behind: "older than this build",
6485
+ }
6486
+ deadline(ms) {
6487
+ const controller = new AbortController();
6488
+ after(this.#clock, ms, this.#disposed.signal, () => {
6489
+ this.#deadlineHit ??= deadlineError(this.#commandLine, ms, WAIT_HINT);
6490
+ controller.abort(this.#deadlineHit);
6491
+ });
6492
+ return AbortSignal.any([this.signal, controller.signal]);
6493
+ }
6494
+ async runToEnd(name, work) {
6495
+ this.#graceCancel.abort();
6496
+ if (this.signal.aborted) this.#finishing(name);
6497
+ const step = work();
6498
+ const settled = step.then(() => {}, () => {});
6499
+ this.#steps.set(settled, name);
6500
+ try {
6501
+ return await step;
6502
+ } finally {
6503
+ this.#steps.delete(settled);
6504
+ this.#startGrace();
6505
+ }
6506
+ }
6507
+ /** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
6508
+ async settle(work) {
6509
+ try {
6510
+ const value = await Promise.race([work, this.#graceOver]);
6511
+ if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
6512
+ return value;
6513
+ } finally {
6514
+ await this.#stepsDone();
6515
+ this.#dispose();
6516
+ }
6517
+ }
6518
+ /**
6519
+ * The error a run ends with: an interrupt or a missed deadline outranks what the Command threw,
6520
+ * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
6521
+ */
6522
+ errorAfter(error) {
6523
+ if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
6524
+ if (this.#deadlineHit !== void 0 && !(error instanceof CliError)) return this.#deadlineHit;
6525
+ return error;
6526
+ }
6527
+ #onStop() {
6528
+ for (const name of this.#steps.values()) this.#finishing(name);
6529
+ this.#startGrace();
6530
+ }
6531
+ #finishing(name) {
6532
+ this.#progress(`finishing ${name}`);
6533
+ }
6534
+ #startGrace() {
6535
+ if (this.signal.aborted && this.#steps.size === 0) {
6536
+ this.#graceCancel.abort();
6537
+ this.#graceCancel = new AbortController();
6538
+ after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
6539
+ }
6540
+ }
6541
+ async #stepsDone() {
6542
+ while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
6543
+ }
6544
+ #dispose() {
6545
+ this.#graceCancel.abort();
6546
+ this.#disposed.abort();
6547
+ this.#outer?.removeEventListener("abort", this.#onOuterAbort);
6548
+ }
6549
+ };
6550
+ //#endregion
6551
+ //#region ../agent-cli/dist/outputs.js
6552
+ const OUTPUT_EXISTS = "OUTPUT_EXISTS";
6553
+ /** Refuses a run that would overwrite existing outputs unless the caller passed `--force`. */
6554
+ async function checkOutputs({ invocation, commandLine, force, files }, outputs) {
6555
+ if (outputs === void 0 || force) return;
6556
+ const found = await Promise.all(outputs.map((path) => files.exists(path)));
6557
+ const existing = outputs.filter((_path, index) => found[index]);
6558
+ if (existing.length === 0) return;
6559
+ const them = existing.length === 1 ? "it" : "them";
6560
+ throw new CliError({
6561
+ code: OUTPUT_EXISTS,
6562
+ message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
6563
+ hint: `to replace ${them}, run \`${commandLineWith(invocation, FORCE_FLAG.flag)}\``
6564
+ });
6565
+ }
6566
+ //#endregion
6567
+ //#region ../agent-cli/dist/project.js
6568
+ const PROJECT_INVALID = "PROJECT_INVALID";
6569
+ function invalid(path, problem, options) {
6570
+ return new CliError({
6571
+ code: PROJECT_INVALID,
6572
+ message: `the Project file ${path} is invalid: ${problem}`,
6573
+ hint: `fix ${path}, then run the command again`
6574
+ }, options);
6575
+ }
6576
+ function readText(path) {
6577
+ try {
6578
+ return readFileSync(path, "utf8");
6579
+ } catch (error) {
6580
+ if (isMissingFile(error)) return void 0;
6581
+ throw error;
6582
+ }
6583
+ }
6584
+ function parsed(path, text, schema) {
6585
+ let json;
6586
+ try {
6587
+ json = JSON.parse(text);
6588
+ } catch (error) {
6589
+ throw invalid(path, messageOf(error), { cause: error });
6590
+ }
6591
+ const result = schema.safeParse(json);
6592
+ if (!result.success) throw invalid(path, prettifyError(result.error));
6593
+ return result.data;
6594
+ }
6595
+ function projectFrom({ file, schema }, folder) {
6596
+ const path = join(folder, file);
6597
+ const text = readText(path);
6598
+ if (text !== void 0) return {
6599
+ root: folder,
6600
+ settings: parsed(path, text, schema)
6601
+ };
6602
+ const parent = dirname(folder);
6603
+ return parent === folder ? void 0 : projectFrom({
6604
+ file,
6605
+ schema
6606
+ }, parent);
6607
+ }
6608
+ /** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
6609
+ function projectOf(projectFile, cwd) {
6610
+ return projectFrom(projectFile, resolve(cwd));
6611
+ }
6612
+ //#endregion
6613
+ //#region ../agent-cli/dist/tools.js
6614
+ const TOOL_MISSING = "TOOL_MISSING";
6615
+ const WINDOWS_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
6616
+ const NOT_EXECUTABLE = /* @__PURE__ */ new Set([
6617
+ "ENOENT",
6618
+ "ENOTDIR",
6619
+ "EACCES"
6620
+ ]);
6621
+ function pathOf(env) {
6622
+ return env[Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? "PATH"] ?? "";
6623
+ }
6624
+ function extensionsOf(env) {
6625
+ return process.platform === "win32" ? (env.PATHEXT ?? WINDOWS_EXTENSIONS).split(";") : [""];
6626
+ }
6627
+ async function isExecutable(file) {
6628
+ try {
6629
+ await access(file, constants$1.X_OK);
6630
+ return true;
6631
+ } catch (error) {
6632
+ if (NOT_EXECUTABLE.has(errnoOf(error) ?? "")) return false;
6633
+ throw error;
6634
+ }
6635
+ }
6636
+ /** Where `name` is found on `PATH` from `env`, or undefined. */
6637
+ async function findTool(name, env) {
6638
+ const candidates = pathOf(env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => extensionsOf(env).map((extension) => join(folder, `${name}${extension}`)));
6639
+ for (const file of candidates) if (await isExecutable(file)) return file;
6640
+ }
6641
+ /** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
6642
+ async function checkTools(tools, env) {
6643
+ const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
6644
+ const missing = tools.filter((_tool, index) => found[index] === void 0);
6645
+ if (missing.length === 0) return;
6646
+ const names = missing.map(({ name }) => name).join(", ");
6647
+ throw new CliError({
6648
+ code: TOOL_MISSING,
6649
+ message: `${missing.length === 1 ? "a tool it runs is" : "tools it runs are"} not on PATH: ${names}`,
6650
+ hint: `install ${missing.length === 1 ? "it" : "them"}, then run the same command again: ${missing.map(({ install }) => install).join("; ")}`
6651
+ });
6652
+ }
6653
+ function toolsIn(node) {
6654
+ return isCommand(node) ? [...node.tools ?? []] : Object.values(node).flatMap(toolsIn);
6655
+ }
6656
+ /** One `doctor` row per distinct tool the Commands declare, with its install command as the fix. */
6657
+ function toolRows(commands) {
6658
+ return [...new Map(toolsIn(commands).map((tool) => [tool.name, tool])).values()].map(({ name, install }) => ({
6659
+ name,
6660
+ run: async (ctx) => {
6661
+ const file = await findTool(name, ctx.env);
6662
+ return file === void 0 ? {
6663
+ state: "problem",
6664
+ message: `${name} is not on PATH`,
6665
+ fix: install
6666
+ } : {
6667
+ state: "ok",
6668
+ message: `found at ${file}`
6669
+ };
6670
+ }
6671
+ }));
6672
+ }
6673
+ //#endregion
6674
+ //#region ../agent-cli/dist/spawn.js
6675
+ const TOOL_FAILED = "TOOL_FAILED";
6676
+ const STDERR_TAIL_LINES = 20;
6677
+ const SIGNAL_EXIT_BASE = 128;
6678
+ const realSpawn = (command, args, { cwd, env, signal }) => new Promise((resolve, reject) => {
6679
+ signal.throwIfAborted();
6680
+ const child = spawn(command, args, {
6681
+ cwd,
6682
+ env,
6683
+ stdio: [
6684
+ "ignore",
6685
+ "pipe",
6686
+ "pipe"
6687
+ ]
6688
+ });
6689
+ const stdout = [];
6690
+ const stderr = [];
6691
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
6692
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
6693
+ const kill = () => {
6694
+ child.kill();
6695
+ };
6696
+ signal.addEventListener("abort", kill, { once: true });
6697
+ child.on("error", (error) => {
6698
+ signal.removeEventListener("abort", kill);
6699
+ reject(error);
6700
+ });
6701
+ child.on("close", (code, killedBy) => {
6702
+ signal.removeEventListener("abort", kill);
6703
+ if (signal.aborted) {
6704
+ reject(signal.reason);
6705
+ return;
6706
+ }
6707
+ resolve({
6708
+ stdout: Buffer.concat(stdout).toString(),
6709
+ stderr: Buffer.concat(stderr).toString(),
6710
+ exitCode: code ?? SIGNAL_EXIT_BASE + (killedBy === null ? 0 : constants.signals[killedBy])
6711
+ });
6712
+ });
6713
+ });
6714
+ function failedError(command, { exitCode, stderr }) {
6715
+ const tail = lastLines(stderr, STDERR_TAIL_LINES);
6716
+ return new CliError({
6717
+ code: TOOL_FAILED,
6718
+ message: tail === "" ? `${command} exited with ${exitCode} and wrote nothing to stderr` : `${command} exited with ${exitCode}; its stderr ends:\n${tail}`,
6719
+ hint: `the output of ${command} above says why it failed`
6720
+ });
6721
+ }
6722
+ function missingError(command, cause) {
6723
+ return new CliError({
6724
+ code: TOOL_MISSING,
6725
+ message: `${command} could not be started: it is not on PATH`,
6726
+ hint: `install ${command}, then run the same command again`
6727
+ }, { cause });
6728
+ }
6729
+ /** `ctx.spawn`: `spawn` bound to the run, its start failures classified by errno. */
6730
+ function contextSpawn(run, defaults, callerLine) {
6731
+ return async (command, args = [], options = {}) => {
6732
+ let result;
6733
+ try {
6734
+ result = await run(command, args, {
6735
+ cwd: options.cwd ?? defaults.cwd,
6736
+ env: options.env ?? defaults.env,
6737
+ signal: defaults.signal
6738
+ });
6739
+ } catch (error) {
6740
+ if (isMissingFile(error)) throw missingError(command, error);
6741
+ throw classifyErrno(error, {
6742
+ path: command,
6743
+ callerLine,
6744
+ env: defaults.env
6745
+ });
6746
+ }
6747
+ if (options.check === true && result.exitCode !== 0) throw failedError(command, result);
6748
+ return result;
6749
+ };
6750
+ }
6751
+ //#endregion
6752
+ //#region ../agent-cli/dist/exit-classes.js
6753
+ const SIGNAL_EXIT_CODES = {
6754
+ SIGINT: 130,
6755
+ SIGTERM: 143
6756
+ };
6757
+ const EXIT_CODES = {
6758
+ caller: 1,
6759
+ operator: 2,
6760
+ source: 3,
6761
+ wait: 75,
6762
+ bug: 70
6763
+ };
6764
+ /** The codes the Runtime and its built-in Commands raise, each with the class that must act on it. */
6765
+ function runtimeCodes() {
6766
+ return {
6767
+ [INVALID_INPUT]: "caller",
6768
+ [UNKNOWN_OPTION]: "caller",
6769
+ [UNKNOWN_COMMAND]: "caller",
6770
+ [UNKNOWN_FIELD]: "caller",
6771
+ [CONFIRMATION_REQUIRED]: "caller",
6772
+ [CONFIRMATION_MISMATCH]: "caller",
6773
+ [NOT_CONFIRMED]: "operator",
6774
+ [OUTPUT_EXISTS]: "caller",
6775
+ [INTERRUPTED]: "interrupted",
6776
+ [DEADLINE_EXCEEDED]: "wait",
6777
+ [INTERNAL_CODE]: "bug",
6778
+ [PERMISSION_DENIED]: "operator",
6779
+ [DISK_FULL]: "operator",
6780
+ [NOT_FOUND]: "caller",
6781
+ [TOOL_FAILED]: "operator",
6782
+ [TOOL_MISSING]: "operator",
6783
+ [PROJECT_INVALID]: "operator",
6784
+ [DOCTOR_PROBLEMS]: "operator",
6785
+ [NOT_GLOBAL_INSTALL]: "operator",
6786
+ [REGISTRY_UNREACHABLE]: "operator",
6787
+ [REGISTRY_AUTH]: "operator",
6788
+ [INSTALL_FAILED]: "operator",
6789
+ [NPM_FAILED]: "operator",
6790
+ [NO_LATEST]: "source"
6791
+ };
6792
+ }
6793
+ /** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
6794
+ function runtimeCodesIn(errors) {
6795
+ return Object.keys(errors).filter((code) => Object.hasOwn(runtimeCodes(), code));
6796
+ }
6797
+ function exitClassOfOne(error, errors) {
6798
+ if (!(error instanceof CliError)) return "bug";
6799
+ if (error instanceof UsageError) return "caller";
6800
+ return runtimeCodes()[error.code] ?? errors[error.code]?.class ?? "bug";
6801
+ }
6802
+ function isBug(error, errors) {
6803
+ return exitClassOfOne(error, errors) === "bug";
6804
+ }
6805
+ const ACT_FIRST = [
6806
+ "caller",
6807
+ "operator",
6808
+ "source",
6809
+ "wait",
6810
+ "interrupted"
6811
+ ];
6812
+ function exitClassOf(error, errors) {
6813
+ const classes = errorsOf(error).map((each) => exitClassOfOne(each, errors));
6814
+ if (classes.includes("bug")) return "bug";
6815
+ return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
6816
+ }
6817
+ /**
6818
+ * The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime's
6819
+ * codes it can reach, and those of its own table.
6820
+ */
6821
+ function reachableExitClasses(cli) {
6822
+ const runtime = [
6823
+ "caller",
6824
+ "operator",
6825
+ "wait",
6826
+ ...cli.commands !== void 0 && cli.install !== void 0 ? ["source"] : []
6827
+ ];
6828
+ const own = Object.values(cli.errors ?? {}).map((declaration) => declaration.class);
6829
+ return /* @__PURE__ */ new Set([...runtime, ...own]);
6830
+ }
6831
+ /** The signal named by an interrupt's abort reason; any other reason counts as Ctrl-C. */
6832
+ function stopSignalOf(reason) {
6833
+ return reason === "SIGTERM" ? "SIGTERM" : "SIGINT";
6834
+ }
6835
+ /**
6836
+ * The exit code of a run that failed with `error`, given the CLI's error table; `stop` is the
6837
+ * signal that interrupted it, if any.
6838
+ */
6839
+ function exitCodeOf(error, errors, stop) {
6840
+ const exitClass = exitClassOf(error, errors);
6841
+ return exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass];
6842
+ }
6843
+ //#endregion
6844
+ //#region ../agent-cli/dist/help.js
6845
+ function table(rows) {
6846
+ const width = Math.max(...rows.map(([left]) => left.length));
6847
+ return rows.map(([left, right]) => right === "" ? ` ${left}` : ` ${left.padEnd(width)} ${right}`);
6848
+ }
6849
+ function section(title, rows, style) {
6850
+ return rows.length === 0 ? [] : [
6851
+ "",
6852
+ style.bold(`${title}:`),
6853
+ ...table(rows)
6854
+ ];
6855
+ }
6856
+ function summaryOf(node) {
6857
+ return node.kind === "command" ? node.description : `group: ${Object.keys(node.commands).join(", ")}`;
6858
+ }
6859
+ function commandRows({ commands }) {
6860
+ return Object.entries(commands).map(([key, node]) => [key, summaryOf(node)]);
6861
+ }
6862
+ function lines(...parts) {
6863
+ return `${parts.join("\n")}\n`;
6864
+ }
6865
+ function listingLines(heading, group, style) {
6866
+ return [
6867
+ style.bold(heading),
6868
+ ...section("Commands", commandRows(group), style),
6869
+ "",
6870
+ `Run \`${group.commandLine} <command> --help\` for its examples and input.`
6871
+ ];
6872
+ }
6873
+ const EXIT_MEANINGS = {
6874
+ caller: "the caller fixes its command",
6875
+ operator: "the operator acts on their machine",
6876
+ source: "someone at the source acts",
6877
+ wait: "nobody acts: wait, then run it again"
6878
+ };
6879
+ function exitCodeRows(cli) {
6880
+ return [
6881
+ ["0", "done"],
6882
+ ...[...[...reachableExitClasses(cli)].map((exitClass) => [EXIT_CODES[exitClass], EXIT_MEANINGS[exitClass]]), [EXIT_CODES.bug, `a bug in ${cli.name}`]].sort(([a], [b]) => a - b).map(([code, meaning]) => [String(code), meaning]),
6883
+ [Object.values(SIGNAL_EXIT_CODES).join(", "), "interrupted"]
6884
+ ];
6885
+ }
6886
+ function firstExample(node) {
6887
+ if (node.kind === "command") return node.examples.slice(0, 1);
6888
+ return Object.values(node.commands).flatMap(firstExample).slice(0, 1);
6889
+ }
6890
+ function rootExamples(root) {
6891
+ if (root.kind === "command") return [...root.examples];
6892
+ const { doctor, ...others } = root.commands;
6893
+ return [...Object.values(others), ...doctor === void 0 ? [] : [doctor]].flatMap(firstExample);
6894
+ }
6895
+ function examplesSection(examples, style) {
6896
+ return section("Examples", examples.map(({ command, note }) => [command, note]), style);
6897
+ }
6898
+ /** The CLI's own help: a listing or its single Command's help, ending with exit codes and examples. */
6899
+ function cliHelp(cli, root, style) {
6900
+ return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${root.commandLine} ${cli.version}`, root, style), ...section("Exit codes", exitCodeRows(cli), style), ...examplesSection(rootExamples(root), style));
6901
+ }
6902
+ function groupHelp(group, style) {
6903
+ return lines(...listingLines(group.commandLine, group, style));
6904
+ }
6905
+ const EFFECT_NOTES = {
6906
+ read: "changes nothing",
6907
+ write: "changes something; show the Plan with --dry-run",
6908
+ destructive: "changes something hard to undo; show the Plan with --dry-run, run with --confirm",
6909
+ serve: "changes nothing; prints its data at once, then runs until Ctrl-C, which exits 0"
6910
+ };
6911
+ function choicesOf(field) {
6912
+ return field.choices === void 0 ? [] : [`one of: ${field.choices.join(", ")}`];
6913
+ }
6914
+ function defaultsOf(field) {
6915
+ if (field.defaultValue === void 0) return [];
6916
+ if (field.type !== "boolean") return [`default: ${JSON.stringify(field.defaultValue)}`];
6917
+ return field.defaultValue === true ? ["on by default; this flag turns it off"] : [];
6918
+ }
6919
+ function fieldRow(field, notes) {
6920
+ const all = [...field.required ? ["required"] : [], ...notes];
6921
+ const suffix = all.length === 0 ? "" : `(${all.join("; ")})`;
6922
+ return [field.usage, [field.description ?? "", suffix].filter((part) => part !== "").join(" ")];
6923
+ }
6924
+ function argRow(field) {
6925
+ return fieldRow(field, [...choicesOf(field), ...defaultsOf(field)]);
6926
+ }
6927
+ function optionRow(field) {
6928
+ return fieldRow(field, defaultsOf(field));
6929
+ }
6930
+ function flagRow({ usage, description }) {
6931
+ return [usage, description];
6932
+ }
6933
+ function commandLines(spec, style, { examples }) {
6934
+ return [
6935
+ style.bold(spec.commandLine),
6936
+ spec.description,
6937
+ ...examples ? examplesSection(spec.examples, style) : [],
6938
+ ...section("Arguments", spec.args.map(argRow), style),
6939
+ ...section("Options", spec.options.map(optionRow), style),
6940
+ "",
6941
+ `${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`,
6942
+ ...section("Standard flags", spec.standardFlags.map(flagRow), style)
6943
+ ];
6944
+ }
6945
+ function commandHelp(spec, style) {
6946
+ return lines(...commandLines(spec, style, { examples: true }));
6947
+ }
6948
+ //#endregion
6949
+ //#region ../agent-cli/dist/help-json.js
6950
+ function inputSchema(schema) {
6951
+ return toJSONSchema(schema, {
6952
+ io: "input",
6953
+ unrepresentable: "any"
6954
+ });
6955
+ }
6956
+ function optionsSchema(options) {
6957
+ return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
6958
+ }
6959
+ function commandData(spec) {
6960
+ return {
6961
+ name: spec.commandLine,
6962
+ description: spec.description,
6963
+ effect: spec.effect,
6964
+ examples: spec.examples,
6965
+ args: inputSchema(spec.command.args ?? object({})),
6966
+ options: optionsSchema(spec.options),
6967
+ standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
6968
+ usage,
6969
+ description,
6970
+ schema: inputSchema(schema)
6971
+ })),
6972
+ output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
6973
+ };
6974
+ }
6975
+ function nodeData(node) {
6976
+ return node.kind === "command" ? commandData(node) : groupData(node);
6977
+ }
6978
+ function groupData(group) {
6979
+ return {
6980
+ name: group.commandLine,
6981
+ commands: Object.values(group.commands).map(nodeData)
6982
+ };
6983
+ }
6984
+ function printed$1(data) {
6985
+ return `${JSON.stringify(data)}\n`;
6986
+ }
6987
+ function errorsData(errors) {
6988
+ return Object.fromEntries(Object.entries(errors).map(([code, declaration]) => [code, {
6989
+ class: declaration.class,
6990
+ details: declaration.details === void 0 ? null : toJSONSchema(declaration.details, { unrepresentable: "any" })
6991
+ }]));
6992
+ }
6993
+ function cliHelpJson({ version, errors }, root) {
6994
+ return printed$1({
6995
+ name: root.commandLine,
6996
+ version,
6997
+ ...nodeData(root),
6998
+ errors: errorsData(errors ?? {})
6999
+ });
7000
+ }
7001
+ function groupHelpJson(group) {
7002
+ return printed$1(groupData(group));
7003
+ }
7004
+ function commandHelpJson(spec) {
7005
+ return printed$1(commandData(spec));
7006
+ }
7007
+ //#endregion
7008
+ //#region ../agent-cli/dist/output.js
7009
+ const SUCCESS_EXIT_CODE = 0;
7010
+ function withTrailingNewline(text) {
7011
+ return text === "" || text.endsWith("\n") ? text : `${text}\n`;
7012
+ }
7013
+ function printed(value, render, { json, invocation, style, fields }) {
7014
+ if (!(value instanceof Page)) return json ? `${JSON.stringify(value)}\n` : withTrailingNewline(render(style));
7015
+ const page = printedPage(value, invocation);
7016
+ if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
7017
+ const rows = projected(page.items, fields);
7018
+ return json ? `${JSON.stringify({
7019
+ ...page,
7020
+ items: rows
7021
+ })}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
7022
+ }
7023
+ function errorText(error, format) {
7024
+ const text = formatRuntimeError(error, {
7025
+ ...format,
7026
+ isBug: (each) => isBug(each, format.errors)
7027
+ });
7028
+ const { usageHelp, json } = format;
7029
+ return usageHelp !== void 0 && !json && error instanceof UsageError ? `${text}\n${usageHelp}` : text;
7030
+ }
7031
+ function stdoutOf({ value, render }, format, spill) {
7032
+ const text = printed(value, render, format);
7033
+ return value instanceof Page ? text : spill(text, format.json);
7034
+ }
7035
+ /** What a serve Command prints the moment it starts. */
7036
+ function startedOutput(outcome, format, spill) {
7037
+ return stdoutOf(outcome, {
7038
+ ...format,
7039
+ fields: void 0
7040
+ }, spill);
7041
+ }
7042
+ /** A serve Command stopped after it started: stopping it is how it is meant to end. */
7043
+ const STOPPED_RESULT = {
7044
+ stdout: "",
7045
+ stderr: "",
7046
+ exitCode: SUCCESS_EXIT_CODE
7047
+ };
7048
+ function errorResult(error, format) {
7049
+ return {
7050
+ stdout: "",
7051
+ stderr: errorText(error, format),
7052
+ exitCode: exitCodeOf(error, format.errors, format.stop)
7053
+ };
7054
+ }
7055
+ async function commandResult(settled, format, spill) {
7056
+ if (settled.ok) try {
7057
+ return {
7058
+ stdout: await stdoutOf(settled.outcome, format, spill),
7059
+ stderr: "",
7060
+ exitCode: SUCCESS_EXIT_CODE
7061
+ };
7062
+ } catch (spillError) {
7063
+ return errorResult(spillError, format);
7064
+ }
7065
+ const { error, delivered } = settled;
7066
+ const result = errorResult(error, format);
7067
+ if (delivered === void 0) return result;
7068
+ try {
7069
+ return {
7070
+ ...result,
7071
+ stdout: await stdoutOf(delivered, format, spill)
7072
+ };
7073
+ } catch (spillError) {
7074
+ const both = new AggregateError([...errorsOf(error), spillError]);
7075
+ return {
7076
+ ...result,
7077
+ stderr: errorText(both, format)
7078
+ };
7079
+ }
7080
+ }
7081
+ function textHelp(node, root, cli, style) {
7082
+ if (node === root) return cliHelp(cli, node, style);
7083
+ return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
7084
+ }
7085
+ function helpOf(node, root, cli, style, json) {
7086
+ if (!json) return textHelp(node, root, cli, style);
7087
+ if (node === root) return cliHelpJson(cli, node);
7088
+ return node.kind === "command" ? commandHelpJson(node) : groupHelpJson(node);
7089
+ }
7090
+ function helpResult(node, root, cli, { style, json }) {
7091
+ return {
7092
+ stdout: helpOf(node, root, cli, style, json),
7093
+ stderr: "",
7094
+ exitCode: SUCCESS_EXIT_CODE
7095
+ };
7096
+ }
7097
+ /** The first line of `--version`: the version, and the commit and time of a stamped build. */
7098
+ function versionLine({ version, build }) {
7099
+ return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
7100
+ }
7101
+ function versionResult(cli, packageRoot) {
7102
+ return {
7103
+ stdout: `${(cli.build === void 0 ? [versionLine(cli)] : [versionLine(cli), `changelog ${join(packageRoot, "CHANGELOG.md")}`]).join("\n")}\n`,
7104
+ stderr: "",
7105
+ exitCode: SUCCESS_EXIT_CODE
7106
+ };
7107
+ }
7108
+ //#endregion
7109
+ //#region ../agent-cli/dist/skills.js
7110
+ /** Where the skills CLI (`npx skills add`) puts a skill, under a project and under home. */
7111
+ const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7112
+ function skillUpdateCommand(cliName) {
7113
+ return `npx -y skills update ${cliName}`;
7114
+ }
7115
+ const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
7116
+ const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
7117
+ /** The version a SKILL.md's frontmatter `metadata.version` states. */
7118
+ function skillVersionIn(skill) {
7119
+ const frontmatter = FRONTMATTER.exec(skill)?.[1];
7120
+ return frontmatter === void 0 ? void 0 : METADATA_VERSION.exec(`${frontmatter}\n`)?.[1];
7121
+ }
7122
+ function homeOf(env) {
7123
+ return env.HOME ?? env.USERPROFILE ?? homedir();
7124
+ }
7125
+ async function copyAt(path, cliVersion) {
7126
+ let skill;
7127
+ try {
7128
+ skill = await readFile(join(path, "SKILL.md"), "utf8");
7129
+ } catch (error) {
7130
+ if (isMissingFile(error)) return;
7131
+ return {
7132
+ path,
7133
+ version: void 0,
7134
+ state: "unreadable"
7135
+ };
7136
+ }
7137
+ const version = skillVersionIn(skill);
7138
+ if (version === void 0 || !isVersion(version) || !isVersion(cliVersion)) return {
7139
+ path,
7140
+ version,
7141
+ state: "unversioned"
7142
+ };
7143
+ return {
7144
+ path,
7145
+ version,
7146
+ state: standingOf(version, cliVersion)
7147
+ };
7148
+ }
7149
+ /** Every installed copy of the skill named after the CLI, in the project first, then under home. */
7150
+ async function skillCopies(skillName, cliVersion, cwd, env) {
7151
+ const paths = [cwd, homeOf(env)].flatMap((root) => SKILL_DIRS.map((dir) => join(root, ...dir, skillName)));
7152
+ const seen = /* @__PURE__ */ new Set();
7153
+ const copies = [];
7154
+ for (const path of paths) {
7155
+ const copy = await copyAt(path, cliVersion);
7156
+ const real = copy === void 0 ? void 0 : await realpath(path);
7157
+ if (copy !== void 0 && real !== void 0 && !seen.has(real)) {
7158
+ seen.add(real);
7159
+ copies.push(copy);
7160
+ }
7161
+ }
7162
+ return copies;
7163
+ }
7164
+ //#endregion
7165
+ //#region ../agent-cli/dist/doctor-rows.js
7166
+ function ok(message) {
7167
+ return {
7168
+ state: "ok",
7169
+ message
7170
+ };
7171
+ }
7172
+ function buildRow(cli) {
7173
+ return {
7174
+ name: "build",
7175
+ run: () => ok(versionLine(cli))
7176
+ };
7177
+ }
7178
+ function nodeRow(cli) {
7179
+ return {
7180
+ name: "node",
7181
+ run: () => {
7182
+ const found = process.versions.node;
7183
+ const range = cli.engines?.node;
7184
+ if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
7185
+ const minimum = engineMinimum(range);
7186
+ if (minimum === void 0) return {
7187
+ state: "problem",
7188
+ message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
7189
+ };
7190
+ if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
7191
+ const major = minimum[0];
7192
+ return {
7193
+ state: "problem",
7194
+ message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
7195
+ fix: `nvm install ${major} && nvm alias default ${major}`
7196
+ };
7197
+ }
7198
+ };
7199
+ }
7200
+ function projectRow(projectFile) {
7201
+ const { file } = projectFile;
7202
+ return {
7203
+ name: "project",
7204
+ run: (ctx) => {
7205
+ let project;
7206
+ try {
7207
+ project = projectOf(projectFile, ctx.cwd);
7208
+ } catch (error) {
7209
+ if (error instanceof CliError && error.code === "PROJECT_INVALID") return {
7210
+ state: "problem",
7211
+ message: error.message
7212
+ };
7213
+ throw error;
7214
+ }
7215
+ return ok(project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(project.root, file));
7216
+ }
7217
+ };
7218
+ }
7219
+ function installRow(cli, bin) {
7220
+ return {
7221
+ name: "install",
7222
+ run: async (ctx) => {
7223
+ const { install } = cli;
7224
+ if (install === void 0) return ok(`${cli.name} declares no install, so it has no \`update\``);
7225
+ try {
7226
+ return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx))).prefix}`);
7227
+ } catch (error) {
7228
+ if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
7229
+ state: "problem",
7230
+ message: error.message,
7231
+ fix: installCommandLine(install)
7232
+ };
7233
+ throw error;
7234
+ }
7235
+ }
7236
+ };
7237
+ }
7238
+ function registryRow(cli) {
7239
+ return {
7240
+ name: "registry",
7241
+ run: async (ctx) => {
7242
+ const { install } = cli;
7243
+ if (install === void 0) return ok(`${cli.name} declares no registry to compare this build with`);
7244
+ const latest = await latestVersion(npmContext(cli, install, ctx));
7245
+ const standing = standingOf(cli.version, latest);
7246
+ const head = `${install.registry} has ${latest} as latest: this build`;
7247
+ if (standing === "behind") return {
7248
+ state: "problem",
7249
+ message: `${head} (${cli.version}) is older`,
7250
+ fix: `${cli.name} update`
7251
+ };
7252
+ return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
7253
+ }
7254
+ };
7255
+ }
7256
+ const SKILL_STATE_NOTES = {
7257
+ current: "the same as this build",
7258
+ behind: "older than this build",
6294
7259
  ahead: "newer than this build",
6295
7260
  unversioned: "states no metadata.version",
6296
7261
  unreadable: "could not be read"
@@ -6318,10 +7283,14 @@ function skillRow(cli) {
6318
7283
  }
6319
7284
  };
6320
7285
  }
6321
- /** The rows every `doctor` reports first, in this order. */
6322
- function fixedRows(cli, bin) {
7286
+ /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
7287
+ function doctorChecks(cli, bin) {
6323
7288
  return [
7289
+ buildRow(cli),
6324
7290
  nodeRow(cli),
7291
+ ...cli.project === void 0 ? [] : [projectRow(cli.project)],
7292
+ ...cli.doctor ?? [],
7293
+ ...toolRows(cli.commands),
6325
7294
  installRow(cli, bin),
6326
7295
  registryRow(cli),
6327
7296
  skillRow(cli)
@@ -6329,7 +7298,8 @@ function fixedRows(cli, bin) {
6329
7298
  }
6330
7299
  //#endregion
6331
7300
  //#region ../agent-cli/dist/doctor.js
6332
- const PROBE_DEADLINE_MS = 1e4;
7301
+ const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
7302
+ const DEFAULT_DEADLINE_MS = 1e4;
6333
7303
  const MS_PER_SECOND = 1e3;
6334
7304
  const MIN_LABEL_WIDTH = 8;
6335
7305
  const reportSchema = object({
@@ -6341,19 +7311,10 @@ const reportSchema = object({
6341
7311
  })),
6342
7312
  next: array(string())
6343
7313
  });
6344
- function untilAborted(signal) {
6345
- return new Promise((_resolve, reject) => {
6346
- if (signal.aborted) {
6347
- reject(signal.reason);
6348
- return;
6349
- }
6350
- signal.addEventListener("abort", () => reject(signal.reason), { once: true });
6351
- });
6352
- }
6353
- function problemOf(check, error, { signal, deadline }) {
7314
+ function problemOf(check, error, { signal, deadline, ms }) {
6354
7315
  if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
6355
7316
  state: "problem",
6356
- message: `${check.name} did not answer within ${PROBE_DEADLINE_MS / MS_PER_SECOND} s`
7317
+ message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
6357
7318
  };
6358
7319
  if (error instanceof CliError) return {
6359
7320
  state: "problem",
@@ -6365,7 +7326,8 @@ function problemOf(check, error, { signal, deadline }) {
6365
7326
  };
6366
7327
  }
6367
7328
  async function rowOf(check, ctx) {
6368
- const signal = ctx.deadline(PROBE_DEADLINE_MS);
7329
+ const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
7330
+ const signal = ctx.deadline(ms);
6369
7331
  let finding;
6370
7332
  try {
6371
7333
  finding = await Promise.race([Promise.resolve().then(() => check.run({
@@ -6375,12 +7337,16 @@ async function rowOf(check, ctx) {
6375
7337
  } catch (error) {
6376
7338
  finding = problemOf(check, error, {
6377
7339
  signal: ctx.signal,
6378
- deadline: signal
7340
+ deadline: signal,
7341
+ ms
6379
7342
  });
6380
7343
  }
6381
7344
  return {
6382
- check: check.name,
6383
- ...finding
7345
+ row: {
7346
+ check: check.name,
7347
+ ...finding
7348
+ },
7349
+ advisory: check.advisory === true
6384
7350
  };
6385
7351
  }
6386
7352
  function nextOf(rows) {
@@ -6394,18 +7360,16 @@ function render({ rows, next }, style) {
6394
7360
  return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
6395
7361
  }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
6396
7362
  }
6397
- function failure(cliName, report) {
6398
- const problems = report.rows.filter(({ state }) => state === "problem").map(({ check }) => check);
6399
- return new FailureWithData({
6400
- code: "DOCTOR_PROBLEMS",
7363
+ function failure(cliName, problems, report) {
7364
+ return new CliError({
7365
+ code: DOCTOR_PROBLEMS,
6401
7366
  message: `${cliName} doctor found ${problems.length === 1 ? "a problem" : `${problems.length} problems`}: ${problems.join(", ")}`,
6402
- hint: report.next.length === 0 ? "each problem row says what is wrong" : "run the commands under next, then run doctor again",
6403
- retryable: false
6404
- }, report);
7367
+ hint: report.next.length === 0 ? "each problem row says what is wrong" : "run the commands under next, then run doctor again"
7368
+ });
6405
7369
  }
6406
- /** The built-in `doctor`: the fixed rows, then the CLI's own, each under its own deadline. */
7370
+ /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
6407
7371
  function doctorCommand(cli, bin) {
6408
- return defineCommand({
7372
+ return defineCommand$1({
6409
7373
  name: "doctor",
6410
7374
  description: "check the setup and say what fixes each problem",
6411
7375
  examples: [{
@@ -6418,120 +7382,24 @@ function doctorCommand(cli, bin) {
6418
7382
  effect: "read",
6419
7383
  output: reportSchema,
6420
7384
  run: async (_input, ctx) => {
6421
- const checks = [...fixedRows(cli, bin), ...cli.doctor ?? []];
6422
- const rows = await Promise.all(checks.map((check) => rowOf(check, ctx)));
7385
+ const checked = await Promise.all(doctorChecks(cli, bin).map((check) => rowOf(check, ctx)));
7386
+ ctx.signal.throwIfAborted();
7387
+ const rows = checked.map(({ row }) => row);
6423
7388
  const report = {
6424
7389
  rows,
6425
7390
  next: nextOf(rows)
6426
7391
  };
6427
- if (rows.some(({ state }) => state === "problem")) throw failure(cli.name, report);
7392
+ const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row.check);
7393
+ if (problems.length > 0) {
7394
+ ctx.deliver(report);
7395
+ throw failure(cli.name, problems, report);
7396
+ }
6428
7397
  return report;
6429
7398
  },
6430
7399
  render
6431
7400
  });
6432
7401
  }
6433
7402
  //#endregion
6434
- //#region ../agent-cli/dist/interrupts.js
6435
- /** How long an interrupted Command may take to settle before the run ends without it. */
6436
- const GRACE_MS = 3e3;
6437
- const GRACE_OVER = Symbol("grace over");
6438
- const INTERRUPTED = "INTERRUPTED";
6439
- function interruptedError() {
6440
- return new CliError({
6441
- code: INTERRUPTED,
6442
- message: "interrupted before it finished",
6443
- hint: "run the command again",
6444
- retryable: true
6445
- });
6446
- }
6447
- function deadlineError(commandLine, ms) {
6448
- return new CliError({
6449
- code: "DEADLINE_EXCEEDED",
6450
- message: `${commandLine} ran past its ${ms} ms deadline`,
6451
- hint: "run it again later, when the service it waits on may answer faster",
6452
- retryable: true
6453
- });
6454
- }
6455
- /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
6456
- var RunControl = class {
6457
- signal;
6458
- #controller = new AbortController();
6459
- #commandLine;
6460
- #outer;
6461
- #onOuterAbort = () => this.#controller.abort(interruptedError());
6462
- #steps = /* @__PURE__ */ new Set();
6463
- #deadlineTimers = /* @__PURE__ */ new Set();
6464
- #deadlineHit;
6465
- #graceTimer;
6466
- #endGrace = () => {};
6467
- #graceOver = new Promise((resolve) => {
6468
- this.#endGrace = resolve;
6469
- });
6470
- constructor(commandLine, outer) {
6471
- this.#commandLine = commandLine;
6472
- this.#outer = outer;
6473
- this.signal = this.#controller.signal;
6474
- this.signal.addEventListener("abort", () => this.#startGrace(), { once: true });
6475
- if (outer?.aborted === true) this.#onOuterAbort();
6476
- else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
6477
- }
6478
- deadline(ms) {
6479
- const controller = new AbortController();
6480
- const timer = setTimeout(() => {
6481
- this.#deadlineHit ??= deadlineError(this.#commandLine, ms);
6482
- controller.abort(this.#deadlineHit);
6483
- }, ms);
6484
- this.#deadlineTimers.add(timer);
6485
- return AbortSignal.any([this.signal, controller.signal]);
6486
- }
6487
- async runToEnd(work) {
6488
- clearTimeout(this.#graceTimer);
6489
- const step = work();
6490
- const settled = step.then(() => {}, () => {});
6491
- this.#steps.add(settled);
6492
- try {
6493
- return await step;
6494
- } finally {
6495
- this.#steps.delete(settled);
6496
- this.#startGrace();
6497
- }
6498
- }
6499
- /** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
6500
- async settle(work) {
6501
- try {
6502
- const value = await Promise.race([work, this.#graceOver]);
6503
- if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
6504
- return value;
6505
- } finally {
6506
- await this.#stepsDone();
6507
- this.#dispose();
6508
- }
6509
- }
6510
- /**
6511
- * The error a run ends with: an interrupt or a missed deadline outranks what the Command threw,
6512
- * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
6513
- */
6514
- errorAfter(error) {
6515
- if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
6516
- if (this.#deadlineHit !== void 0 && !(error instanceof CliError)) return this.#deadlineHit;
6517
- return error;
6518
- }
6519
- #startGrace() {
6520
- if (this.signal.aborted && this.#steps.size === 0) {
6521
- clearTimeout(this.#graceTimer);
6522
- this.#graceTimer = setTimeout(() => this.#endGrace(GRACE_OVER), GRACE_MS);
6523
- }
6524
- }
6525
- async #stepsDone() {
6526
- while (this.#steps.size > 0) await Promise.all(this.#steps);
6527
- }
6528
- #dispose() {
6529
- clearTimeout(this.#graceTimer);
6530
- for (const timer of this.#deadlineTimers) clearTimeout(timer);
6531
- this.#outer?.removeEventListener("abort", this.#onOuterAbort);
6532
- }
6533
- };
6534
- //#endregion
6535
7403
  //#region ../agent-cli/dist/upgrade-notes.js
6536
7404
  const RELEASE_HEADING = /^## v?(\S+)/;
6537
7405
  function releasesIn(changelog) {
@@ -6595,17 +7463,16 @@ function renderUpdate(cli, install, update) {
6595
7463
  ...update.next.map((command) => `next: ${command}, so the ${cli.name} skill describes ${installed.version}`)
6596
7464
  ].join("\n");
6597
7465
  }
6598
- function interruptedAfterInstall(plan, update) {
6599
- return new FailureWithData({
7466
+ function interruptedAfterInstall(plan) {
7467
+ return new CliError({
6600
7468
  code: INTERRUPTED,
6601
7469
  message: `interrupted, but ${plan.package}@${plan.latest} had already been installed with the npm under ${plan.prefix}, replacing ${plan.from}`,
6602
- hint: "nothing to run again: run the commands under next",
6603
- retryable: false
6604
- }, update);
7470
+ hint: "nothing to run again: run the commands under next"
7471
+ });
6605
7472
  }
6606
7473
  /** The built-in `update`, for a CLI installed globally with npm. */
6607
7474
  function updateCommand(cli, install, bin) {
6608
- return defineCommand({
7475
+ return defineCommand$1({
6609
7476
  name: "update",
6610
7477
  description: "install the latest version of this CLI from its registry",
6611
7478
  examples: [{
@@ -6639,7 +7506,7 @@ function updateCommand(cli, install, bin) {
6639
7506
  };
6640
7507
  ctx.signal.throwIfAborted();
6641
7508
  ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
6642
- await ctx.runToEnd(() => installGlobally(latest, npmContext(cli, install, ctx)));
7509
+ await ctx.runToEnd(`installing ${plan.package}@${latest}`, () => installGlobally(latest, npmContext(cli, install, ctx)));
6643
7510
  const update = {
6644
7511
  from,
6645
7512
  latest,
@@ -6650,7 +7517,10 @@ function updateCommand(cli, install, bin) {
6650
7517
  upgrades: await upgradeNotes(plan.packageDir, from, latest),
6651
7518
  next: [skillUpdateCommand(cli.name)]
6652
7519
  };
6653
- if (ctx.signal.aborted) throw interruptedAfterInstall(plan, update);
7520
+ if (ctx.signal.aborted) {
7521
+ ctx.deliver(update);
7522
+ throw interruptedAfterInstall(plan);
7523
+ }
6654
7524
  return update;
6655
7525
  },
6656
7526
  render: (update) => renderUpdate(cli, install, update),
@@ -6666,263 +7536,208 @@ function builtInCommands(cli, bin) {
6666
7536
  ...cli.install === void 0 ? {} : { update: updateCommand(cli, cli.install, bin) }
6667
7537
  };
6668
7538
  }
6669
- function defineCli(cli) {
7539
+ //#endregion
7540
+ //#region ../agent-cli/dist/agent-cli.js
7541
+ function checkOwnCodes(cli) {
7542
+ const redeclared = runtimeCodesIn(cli.errors ?? {});
7543
+ if (redeclared.length > 0) throw new Error(`${cli.name} declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
7544
+ }
7545
+ function defineCli$1(cli) {
7546
+ checkOwnCodes(cli);
6670
7547
  if (cli.commands === void 0) return cli;
6671
7548
  const { commands } = cli;
6672
7549
  const taken = Object.keys(builtInCommands(cli, void 0)).filter((name) => commands[name] !== void 0);
6673
7550
  if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
6674
- return cli;
6675
- }
6676
- //#endregion
6677
- //#region ../agent-cli/dist/command-spec.js
6678
- function commandSpec(commandLine, command) {
6679
- const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
6680
- return {
6681
- kind: "command",
6682
- commandLine,
6683
- command,
6684
- description: command.description,
6685
- effect: command.effect,
6686
- examples: command.examples,
6687
- args: argFields(command.args),
6688
- options: optionFields(command.options),
6689
- standardFlags: standardFlagsFor(command.effect, list),
6690
- list,
6691
- output: list?.output ?? command.output
6692
- };
6693
- }
6694
- function groupSpec(commandLine, commands) {
6695
- return {
6696
- kind: "group",
6697
- commandLine,
6698
- commands: Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node)]))
6699
- };
6700
- }
6701
- function specOf(commandLine, node) {
6702
- return isCommand(node) ? commandSpec(commandLine, node) : groupSpec(commandLine, node);
6703
- }
6704
- //#endregion
6705
- //#region ../agent-cli/dist/confirm.js
6706
- function checkConfirmation({ invocation, commandLine, given }, expected) {
6707
- if (given === expected) return;
6708
- const hint = `to confirm, run \`${commandLineWith(invocation, CONFIRM_FLAG.flag, expected)}\``;
6709
- if (given === void 0) throw new UsageError({
6710
- code: "CONFIRMATION_REQUIRED",
6711
- message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
6712
- hint
6713
- });
6714
- throw new UsageError({
6715
- code: "CONFIRMATION_MISMATCH",
6716
- message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
6717
- hint
6718
- });
6719
- }
6720
- //#endregion
6721
- //#region ../agent-cli/dist/execute.js
6722
- function outcomeOf(value, render) {
6723
- return {
6724
- value,
6725
- render: (style) => render(value, style)
6726
- };
6727
- }
6728
- /** How the Command prints data it fails with, which is the data its `run` or `apply` returns. */
6729
- function renderOf(command) {
6730
- return (data, style) => command.render(data, style);
6731
- }
6732
- async function execute(command, input, ctx, { dryRun, confirm, page, invocation, commandLine }) {
6733
- if (command.rows !== void 0) return outcomeOf(await command.run({
6734
- ...input,
6735
- ...page
6736
- }, ctx), (data, style) => command.render(data, style));
6737
- if (command.effect === "read") return outcomeOf(await command.run(input, ctx), (data, style) => command.render(data, style));
6738
- const plan = await command.plan(input, ctx);
6739
- if (dryRun) return outcomeOf(plan, (value, style) => command.renderPlan(value, style));
6740
- if (command.effect === "destructive") checkConfirmation({
6741
- invocation,
6742
- commandLine,
6743
- given: confirm
6744
- }, command.confirm(plan));
6745
- return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
6746
- }
6747
- //#endregion
6748
- //#region ../agent-cli/dist/help.js
6749
- function table(rows) {
6750
- const width = Math.max(...rows.map(([left]) => left.length));
6751
- return rows.map(([left, right]) => right === "" ? ` ${left}` : ` ${left.padEnd(width)} ${right}`);
6752
- }
6753
- function section(title, rows, style) {
6754
- return rows.length === 0 ? [] : [
6755
- "",
6756
- style.bold(`${title}:`),
6757
- ...table(rows)
6758
- ];
6759
- }
6760
- function summaryOf(node) {
6761
- return node.kind === "command" ? node.description : `group: ${Object.keys(node.commands).join(", ")}`;
6762
- }
6763
- function commandRows({ commands }) {
6764
- return Object.entries(commands).map(([key, node]) => [key, summaryOf(node)]);
6765
- }
6766
- function lines(...parts) {
6767
- return `${parts.join("\n")}\n`;
6768
- }
6769
- function listing(heading, group, style) {
6770
- return lines(style.bold(heading), ...section("Commands", commandRows(group), style), "", `Run \`${group.commandLine} <command> --help\` for its examples and input.`);
6771
- }
6772
- function cliHelp(version, root, style) {
6773
- return listing(`${root.commandLine} ${version}`, root, style);
6774
- }
6775
- function groupHelp(group, style) {
6776
- return listing(group.commandLine, group, style);
6777
- }
6778
- const EFFECT_NOTES = {
6779
- read: "changes nothing",
6780
- write: "changes something; show the Plan with --dry-run",
6781
- destructive: "changes something hard to undo; show the Plan with --dry-run, run with --confirm"
6782
- };
6783
- function choicesOf(field) {
6784
- return field.choices === void 0 ? [] : [`one of: ${field.choices.join(", ")}`];
6785
- }
6786
- function defaultsOf(field) {
6787
- if (field.defaultValue === void 0) return [];
6788
- if (field.type !== "boolean") return [`default: ${JSON.stringify(field.defaultValue)}`];
6789
- return field.defaultValue === true ? ["on by default; this flag turns it off"] : [];
6790
- }
6791
- function fieldRow(field, notes) {
6792
- const all = [...field.required ? ["required"] : [], ...notes];
6793
- const suffix = all.length === 0 ? "" : `(${all.join("; ")})`;
6794
- return [field.usage, [field.description ?? "", suffix].filter((part) => part !== "").join(" ")];
6795
- }
6796
- function argRow(field) {
6797
- return fieldRow(field, [...choicesOf(field), ...defaultsOf(field)]);
6798
- }
6799
- function optionRow(field) {
6800
- return fieldRow(field, defaultsOf(field));
6801
- }
6802
- function flagRow({ usage, description }) {
6803
- return [usage, description];
6804
- }
6805
- function commandHelp(spec, style) {
6806
- return lines(style.bold(spec.commandLine), spec.description, ...section("Examples", spec.examples.map(({ command, note }) => [command, note]), style), ...section("Arguments", spec.args.map(argRow), style), ...section("Options", spec.options.map(optionRow), style), "", `${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`, ...section("Standard flags", spec.standardFlags.map(flagRow), style));
6807
- }
6808
- //#endregion
6809
- //#region ../agent-cli/dist/help-json.js
6810
- function inputSchema(schema) {
6811
- return toJSONSchema(schema, {
6812
- io: "input",
6813
- unrepresentable: "any"
6814
- });
6815
- }
6816
- function optionsSchema(options) {
6817
- return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
7551
+ return cli;
6818
7552
  }
6819
- function commandData(spec) {
7553
+ /**
7554
+ * Declares the CLI's ports and its Project file's settings once; the `defineCommand` and
7555
+ * `defineCli` it returns type `ctx.ports` and `ctx.project`.
7556
+ */
7557
+ function agentCli() {
6820
7558
  return {
6821
- name: spec.commandLine,
6822
- description: spec.description,
6823
- effect: spec.effect,
6824
- examples: spec.examples,
6825
- args: inputSchema(spec.command.args ?? object({})),
6826
- options: optionsSchema(spec.options),
6827
- standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
6828
- usage,
6829
- description,
6830
- schema: inputSchema(schema)
6831
- })),
6832
- output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
7559
+ defineCommand: defineCommandFor(),
7560
+ defineCli: defineCli$1
6833
7561
  };
6834
7562
  }
6835
- function nodeData(node) {
6836
- return node.kind === "command" ? commandData(node) : groupData(node);
7563
+ //#endregion
7564
+ //#region ../agent-cli/dist/error-table.js
7565
+ /** Keeps each code's literal class and `details` schema, so `cliError` can type its throw sites. */
7566
+ function defineErrors(table) {
7567
+ return table;
6837
7568
  }
6838
- function groupData(group) {
7569
+ /** A `CliError` for a code of `table`, whose `details` must match the code's schema. */
7570
+ function cliError(_table, code, init, options) {
7571
+ return new CliError({
7572
+ ...init,
7573
+ code
7574
+ }, options);
7575
+ }
7576
+ //#endregion
7577
+ //#region ../agent-cli/dist/command-spec.js
7578
+ function declaredOf(command) {
7579
+ const planned = command.effect === "write" || command.effect === "destructive";
6839
7580
  return {
6840
- name: group.commandLine,
6841
- commands: Object.values(group.commands).map(nodeData)
7581
+ outputs: planned && command.outputs !== void 0,
7582
+ cost: planned && command.cost !== void 0
6842
7583
  };
6843
7584
  }
6844
- function printed$1(data) {
6845
- return `${JSON.stringify(data)}\n`;
6846
- }
6847
- function cliHelpJson(version, root) {
6848
- return printed$1({
6849
- name: root.commandLine,
6850
- version,
6851
- ...nodeData(root)
6852
- });
7585
+ function commandSpec(commandLine, command) {
7586
+ const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
7587
+ return {
7588
+ kind: "command",
7589
+ commandLine,
7590
+ command,
7591
+ description: command.description,
7592
+ effect: command.effect,
7593
+ examples: command.examples,
7594
+ args: argFields(command.args),
7595
+ options: optionFields(command.options),
7596
+ standardFlags: standardFlagsFor(command.effect, declaredOf(command), list),
7597
+ list,
7598
+ output: list?.output ?? command.output
7599
+ };
6853
7600
  }
6854
- function groupHelpJson(group) {
6855
- return printed$1(groupData(group));
7601
+ function groupSpec(commandLine, commands) {
7602
+ return {
7603
+ kind: "group",
7604
+ commandLine,
7605
+ commands: Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node)]))
7606
+ };
6856
7607
  }
6857
- function commandHelpJson(spec) {
6858
- return printed$1(commandData(spec));
7608
+ function specOf(commandLine, node) {
7609
+ return isCommand(node) ? commandSpec(commandLine, node) : groupSpec(commandLine, node);
6859
7610
  }
6860
7611
  //#endregion
6861
- //#region ../agent-cli/dist/output.js
6862
- const SUCCESS_EXIT_CODE = 0;
6863
- const FAILURE_EXIT_CODE = 1;
6864
- const USAGE_EXIT_CODE = 2;
6865
- function withTrailingNewline(text) {
6866
- return text === "" || text.endsWith("\n") ? text : `${text}\n`;
7612
+ //#region ../agent-cli/dist/execute.js
7613
+ function outcomeOf(value, render) {
7614
+ return {
7615
+ value,
7616
+ render: (style) => render(value, style)
7617
+ };
6867
7618
  }
6868
- function printed(value, render, { json, invocation, style, fields }) {
6869
- if (!(value instanceof Page)) return json ? `${JSON.stringify(value)}\n` : withTrailingNewline(render(style));
6870
- const page = printedPage(value, invocation);
6871
- if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
6872
- const rows = projected(page.items, fields);
6873
- return json ? `${JSON.stringify({
6874
- ...page,
6875
- items: rows
6876
- })}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
7619
+ /** Delivered data as the Command prints it, which is the data its `run` or `apply` returns. */
7620
+ function deliveredOutcome(command, data) {
7621
+ return outcomeOf(data, command.render);
6877
7622
  }
6878
- function errorResult(error, format) {
7623
+ function dryRunOutcome(command, plan) {
7624
+ const renderPlan = (style) => command.renderPlan(plan, style);
7625
+ if (command.outputs === void 0 && command.cost === void 0) return {
7626
+ value: plan,
7627
+ render: renderPlan
7628
+ };
7629
+ const outputs = command.outputs?.(plan);
7630
+ const cost = command.cost?.(plan);
6879
7631
  return {
6880
- stdout: "",
6881
- stderr: formatRuntimeError(error, format),
6882
- exitCode: error instanceof UsageError ? USAGE_EXIT_CODE : FAILURE_EXIT_CODE
7632
+ value: {
7633
+ plan,
7634
+ ...outputs === void 0 ? {} : { outputs },
7635
+ ...command.cost === void 0 ? {} : { cost: cost ?? null }
7636
+ },
7637
+ render: (style) => [
7638
+ renderPlan(style),
7639
+ ...outputs === void 0 ? [] : ["outputs:", ...outputs.map((path) => ` ${path}`)],
7640
+ ...cost === void 0 ? [] : [`cost: ${cost}`]
7641
+ ].join("\n")
6883
7642
  };
6884
7643
  }
6885
- function commandResult(settled, format) {
6886
- if (settled.ok) {
6887
- const { value, render } = settled.outcome;
6888
- return {
6889
- stdout: printed(value, render, format),
6890
- stderr: "",
6891
- exitCode: SUCCESS_EXIT_CODE
6892
- };
7644
+ async function execute(command, input, ctx, { dryRun, confirm, yes, force, page, invocation, commandLine, started }) {
7645
+ await checkTools(command.tools ?? [], ctx.env);
7646
+ if (command.rows !== void 0) return outcomeOf(await command.run({
7647
+ ...input,
7648
+ ...page
7649
+ }, ctx), (data, style) => command.render(data, style));
7650
+ if (command.effect === "read") return outcomeOf(await command.run(input, ctx), (data, style) => command.render(data, style));
7651
+ if (command.effect === "serve") {
7652
+ await started(outcomeOf(await command.start(input, ctx), (value, style) => command.render(value, style)));
7653
+ return untilAborted(ctx.signal);
6893
7654
  }
6894
- const { error, render } = settled;
6895
- const result = errorResult(error, format);
6896
- if (!(error instanceof FailureWithData)) return result;
6897
- const stdout = printed(error.data, (style) => render(error.data, style), format);
6898
- return {
6899
- ...result,
6900
- stdout
6901
- };
7655
+ const plan = await command.plan(input, ctx);
7656
+ if (dryRun) return dryRunOutcome(command, plan);
7657
+ await checkOutputs({
7658
+ invocation,
7659
+ commandLine,
7660
+ force,
7661
+ files: ctx.files
7662
+ }, command.outputs?.(plan));
7663
+ checkCost({
7664
+ invocation,
7665
+ commandLine,
7666
+ yes
7667
+ }, command.cost?.(plan));
7668
+ if (command.effect === "destructive") checkConfirmation({
7669
+ invocation,
7670
+ commandLine,
7671
+ given: confirm
7672
+ }, command.confirm(plan));
7673
+ return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
6902
7674
  }
6903
- function helpOf(node, root, cli, style, json) {
6904
- if (json) {
6905
- if (node === root) return cliHelpJson(cli.version, node);
6906
- return node.kind === "command" ? commandHelpJson(node) : groupHelpJson(node);
7675
+ //#endregion
7676
+ //#region ../agent-cli/dist/files.js
7677
+ const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
7678
+ function tempPathOf(path) {
7679
+ return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
7680
+ }
7681
+ function isAlive(pid) {
7682
+ try {
7683
+ process.kill(pid, 0);
7684
+ return true;
7685
+ } catch (error) {
7686
+ return errnoOf(error) === "EPERM";
6907
7687
  }
6908
- if (node.kind === "command") return commandHelp(node, style);
6909
- return node === root ? cliHelp(cli.version, node, style) : groupHelp(node, style);
6910
7688
  }
6911
- function helpResult(node, root, cli, { style, json }) {
6912
- return {
6913
- stdout: helpOf(node, root, cli, style, json),
6914
- stderr: "",
6915
- exitCode: SUCCESS_EXIT_CODE
6916
- };
7689
+ async function sweep(folder) {
7690
+ const dead = (await readdir(folder)).filter((name) => {
7691
+ const pid = TEMP_FILE.exec(name)?.[1];
7692
+ return pid !== void 0 && !isAlive(Number(pid));
7693
+ });
7694
+ await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
6917
7695
  }
6918
- function versionResult({ version, build }) {
7696
+ /** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
7697
+ function realFiles() {
7698
+ const swept = /* @__PURE__ */ new Set();
6919
7699
  return {
6920
- stdout: `${build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""})`}\n`,
6921
- stderr: "",
6922
- exitCode: SUCCESS_EXIT_CODE
7700
+ exists: async (path) => {
7701
+ try {
7702
+ await access(path);
7703
+ return true;
7704
+ } catch (error) {
7705
+ if (isMissingFile(error)) return false;
7706
+ throw error;
7707
+ }
7708
+ },
7709
+ write: async (path, data) => {
7710
+ const folder = dirname(path);
7711
+ if (!swept.has(folder)) {
7712
+ await sweep(folder);
7713
+ swept.add(folder);
7714
+ }
7715
+ const temp = tempPathOf(path);
7716
+ try {
7717
+ await writeFile(temp, data);
7718
+ await rename(temp, path);
7719
+ } catch (error) {
7720
+ await rm(temp, { force: true });
7721
+ throw error;
7722
+ }
7723
+ }
6923
7724
  };
6924
7725
  }
6925
7726
  //#endregion
7727
+ //#region ../agent-cli/dist/package-root.js
7728
+ /**
7729
+ * The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
7730
+ * from `cwd`; the search's start when none holds one. The bin is resolved through links first,
7731
+ * since npm's global bin is a link to it.
7732
+ */
7733
+ function packageRootOf(bin, cwd) {
7734
+ const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
7735
+ for (let dir = start;; dir = dirname(dir)) {
7736
+ if (existsSync(join(dir, "package.json"))) return dir;
7737
+ if (dirname(dir) === dir) return start;
7738
+ }
7739
+ }
7740
+ //#endregion
6926
7741
  //#region ../agent-cli/dist/suggest.js
6927
7742
  const MAX_DISTANCE = 3;
6928
7743
  const MIN_SIMILARITY = .4;
@@ -7047,40 +7862,6 @@ function tokenize(words, known) {
7047
7862
  };
7048
7863
  }
7049
7864
  //#endregion
7050
- //#region ../agent-cli/dist/usage.js
7051
- function unknownError({ code, problem, commandLine, corrections, listed }) {
7052
- return new UsageError({
7053
- code,
7054
- message: `${problem} for \`${commandLine}\``,
7055
- hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
7056
- });
7057
- }
7058
- function unknownOptionError(commandLine, invocation, flag, suggestions) {
7059
- return unknownError({
7060
- code: "UNKNOWN_OPTION",
7061
- problem: `unknown option '${flag}'`,
7062
- commandLine,
7063
- corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
7064
- listed: "options"
7065
- });
7066
- }
7067
- function unknownCommandError(commandLine, name, suggestions) {
7068
- return unknownError({
7069
- code: "UNKNOWN_COMMAND",
7070
- problem: `unknown command '${name}'`,
7071
- commandLine,
7072
- corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
7073
- listed: "commands"
7074
- });
7075
- }
7076
- function invalidInputError(commandLine, problem) {
7077
- return new UsageError({
7078
- code: "INVALID_INPUT",
7079
- message: problem,
7080
- hint: `run \`${commandLine} --help\` to see the expected input`
7081
- });
7082
- }
7083
- //#endregion
7084
7865
  //#region ../agent-cli/dist/parse-input.js
7085
7866
  const NO_INPUT = object({});
7086
7867
  function knownFlags(spec) {
@@ -7137,7 +7918,7 @@ function invalidInput(commandLine, invocation, unknown, otherLines) {
7137
7918
  const renames = new Map(unknown.flatMap(({ flag, suggestion }) => suggestion === void 0 ? [] : [[flag, suggestion]]));
7138
7919
  const correctable = otherLines.length === 0 && renames.size === unknown.length;
7139
7920
  return new UsageError({
7140
- code: "INVALID_INPUT",
7921
+ code: INVALID_INPUT,
7141
7922
  message: [
7142
7923
  `invalid input for \`${commandLine}\``,
7143
7924
  ...unknown.map(unknownLine),
@@ -7190,6 +7971,8 @@ function parseCommand({ spec, words, json }, invocation) {
7190
7971
  json,
7191
7972
  dryRun: values.get(DRY_RUN_FLAG.flag) === true,
7192
7973
  confirm: typeof confirm === "string" ? confirm : void 0,
7974
+ yes: values.get(YES_FLAG.flag) === true,
7975
+ force: values.get(FORCE_FLAG.flag) === true,
7193
7976
  page,
7194
7977
  fields: spec.list === void 0 ? void 0 : selectedFields(values, spec.list.rowFields, spec.commandLine)
7195
7978
  }
@@ -7244,6 +8027,7 @@ function walk(node, words, json, invocation) {
7244
8027
  return {
7245
8028
  kind: "usage error",
7246
8029
  error: unknownOptionError(node.commandLine, invocation, first, suggestions),
8030
+ node,
7247
8031
  json
7248
8032
  };
7249
8033
  }
@@ -7254,6 +8038,7 @@ function unknownCommand(group, name, json) {
7254
8038
  return {
7255
8039
  kind: "usage error",
7256
8040
  error: unknownCommandError(group.commandLine, name, suggestions),
8041
+ node: group,
7257
8042
  json
7258
8043
  };
7259
8044
  }
@@ -7269,19 +8054,94 @@ function route(root, invocation) {
7269
8054
  return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
7270
8055
  }
7271
8056
  //#endregion
8057
+ //#region ../agent-cli/dist/secrets.js
8058
+ function dotenvPath(cliName, env) {
8059
+ const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
8060
+ return join(config, cliName, ".env");
8061
+ }
8062
+ function readDotenv(path) {
8063
+ try {
8064
+ return parseEnv(readFileSync(path, "utf8"));
8065
+ } catch (error) {
8066
+ if (isMissingFile(error)) return {};
8067
+ throw error;
8068
+ }
8069
+ }
8070
+ /** Looks `name` up in the env, then in `$XDG_CONFIG_HOME/<cli>/.env`; an empty value counts as unset. */
8071
+ function secretOf(cliName, env, name) {
8072
+ const fromEnv = env[name];
8073
+ if (fromEnv) return {
8074
+ value: fromEnv,
8075
+ origin: { kind: "env" }
8076
+ };
8077
+ const path = dotenvPath(cliName, env);
8078
+ const fromFile = readDotenv(path)[name];
8079
+ return fromFile ? {
8080
+ value: fromFile,
8081
+ origin: {
8082
+ kind: "file",
8083
+ path
8084
+ }
8085
+ } : void 0;
8086
+ }
8087
+ //#endregion
8088
+ //#region ../agent-cli/dist/spill.js
8089
+ const STDOUT_BUDGET_CHARS = 3e4;
8090
+ const SPILL_RETENTION_MS = 6048e5;
8091
+ const PREVIEW_CHARS = 2e3;
8092
+ const SPILL_FILE = /^\d{4}-\d\d-\d\dT\d\d-\d\d-\d\d\.\d{3}Z-.+\.(json|txt)$/;
8093
+ function spillName(now, commandName, json) {
8094
+ return `${new Date(now).toISOString().replaceAll(":", "-")}-${commandName}.${json ? "json" : "txt"}`;
8095
+ }
8096
+ async function pruneOne(path, cutoff) {
8097
+ try {
8098
+ if ((await stat(path)).mtimeMs < cutoff) await rm(path, { force: true });
8099
+ } catch (error) {
8100
+ if (!isMissingFile(error)) throw error;
8101
+ }
8102
+ }
8103
+ async function pruneOld(folder, cutoff) {
8104
+ const names = (await readdir(folder)).filter((name) => SPILL_FILE.test(name));
8105
+ await Promise.all(names.map((name) => pruneOne(join(folder, name), cutoff)));
8106
+ }
8107
+ async function prepare(folder, cutoff, target) {
8108
+ try {
8109
+ await mkdir(folder, { recursive: true });
8110
+ await pruneOld(folder, cutoff);
8111
+ } catch (error) {
8112
+ throw classifyErrno(error, {
8113
+ path: folder,
8114
+ callerLine: target.callerLine,
8115
+ env: target.env
8116
+ });
8117
+ }
8118
+ }
8119
+ function tempRoot(env) {
8120
+ const { TMPDIR } = env;
8121
+ return TMPDIR === void 0 || TMPDIR === "" ? tmpdir() : TMPDIR;
8122
+ }
8123
+ function spillTo(target) {
8124
+ return async (printed, json) => {
8125
+ if (printed.length <= STDOUT_BUDGET_CHARS) return printed;
8126
+ const now = target.clock.now();
8127
+ const folder = join(tempRoot(target.env), target.cliName);
8128
+ await prepare(folder, now - SPILL_RETENTION_MS, target);
8129
+ const file = join(folder, spillName(now, target.commandName, json));
8130
+ await target.files.write(file, printed);
8131
+ const bytes = Buffer.byteLength(printed);
8132
+ const preview = printed.slice(0, PREVIEW_CHARS);
8133
+ return json ? `${JSON.stringify({
8134
+ file,
8135
+ bytes,
8136
+ preview
8137
+ })}\n` : `file: ${file}\nbytes: ${bytes}\npreview:\n${preview}\n`;
8138
+ };
8139
+ }
8140
+ //#endregion
7272
8141
  //#region ../agent-cli/dist/style.js
7273
- const AGENT_VARIABLES = [
7274
- "CLAUDECODE",
7275
- "CODEX_CI",
7276
- "CODEX_SANDBOX",
7277
- "GEMINI_CLI",
7278
- "CURSOR_AGENT",
7279
- "CI"
7280
- ];
7281
8142
  function colourEnabled(env, stdoutIsTTY) {
7282
- const noColor = env.NO_COLOR !== void 0 && env.NO_COLOR !== "";
7283
- const agent = AGENT_VARIABLES.some((name) => env[name] !== void 0);
7284
- return stdoutIsTTY && !noColor && !agent;
8143
+ if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
8144
+ return env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0" || stdoutIsTTY && !isAgent(env);
7285
8145
  }
7286
8146
  function sgr(open, close) {
7287
8147
  return (text) => `\u001b[${open}m${text}\u001b[${close}m`;
@@ -7308,7 +8168,36 @@ function styleFor(env, stdoutIsTTY) {
7308
8168
  }
7309
8169
  //#endregion
7310
8170
  //#region ../agent-cli/dist/run-cli.js
7311
- async function runCommand(routed, { base, signal, format: baseFormat }) {
8171
+ /** `files` resolving paths against `cwd`, its failures classified by errno. */
8172
+ function classifiedFiles(files, base, callerLine) {
8173
+ return {
8174
+ exists: async (path) => {
8175
+ const target = resolve(base.cwd, path);
8176
+ try {
8177
+ return await files.exists(target);
8178
+ } catch (error) {
8179
+ throw classifyErrno(error, {
8180
+ path: target,
8181
+ callerLine,
8182
+ env: base.env
8183
+ });
8184
+ }
8185
+ },
8186
+ write: async (path, data) => {
8187
+ const target = resolve(base.cwd, path);
8188
+ try {
8189
+ await files.write(target, data);
8190
+ } catch (error) {
8191
+ throw classifyErrno(error, {
8192
+ path: target,
8193
+ callerLine,
8194
+ env: base.env
8195
+ });
8196
+ }
8197
+ }
8198
+ };
8199
+ }
8200
+ async function runCommand(routed, { base, writeStdout, signal, format: baseFormat, ports, clock, files, spawn, projectFile }) {
7312
8201
  const { command, commandLine } = routed.spec;
7313
8202
  const format = {
7314
8203
  ...baseFormat,
@@ -7316,19 +8205,55 @@ async function runCommand(routed, { base, signal, format: baseFormat }) {
7316
8205
  };
7317
8206
  const { invocation } = format;
7318
8207
  let parsed;
8208
+ let project;
7319
8209
  try {
7320
8210
  parsed = parseCommand(routed, invocation);
8211
+ project = projectFile === void 0 ? void 0 : projectOf(projectFile, base.cwd);
7321
8212
  } catch (error) {
7322
8213
  return errorResult(error, format);
7323
8214
  }
7324
8215
  const { input, flags } = parsed;
7325
- const control = new RunControl(commandLine, signal);
8216
+ const control = new RunControl({
8217
+ commandLine,
8218
+ outer: signal,
8219
+ clock,
8220
+ progress: base.progress,
8221
+ runDeadline: command.deadline === void 0 ? void 0 : {
8222
+ ms: command.deadline.ms,
8223
+ hint: command.deadline.resume(input)
8224
+ }
8225
+ });
8226
+ let delivered;
8227
+ const callerLine = commandLineOf(invocation);
8228
+ const classified = classifiedFiles(files, base, callerLine);
8229
+ const spill = spillTo({
8230
+ cliName: format.cliName,
8231
+ commandName: command.name,
8232
+ env: base.env,
8233
+ clock,
8234
+ files: classified,
8235
+ callerLine
8236
+ });
7326
8237
  const ctx = {
7327
8238
  ...base,
7328
8239
  signal: control.signal,
7329
8240
  deadline: (ms) => control.deadline(ms),
7330
- runToEnd: (work) => control.runToEnd(work)
8241
+ runToEnd: (name, work) => control.runToEnd(name, work),
8242
+ ports: ports(),
8243
+ clock,
8244
+ files: classified,
8245
+ spawn: contextSpawn(spawn, {
8246
+ cwd: base.cwd,
8247
+ env: base.env,
8248
+ signal: control.signal
8249
+ }, callerLine),
8250
+ project,
8251
+ poll: (check, { intervalMs }) => poll(clock, check, intervalMs, control.signal),
8252
+ deliver: (data) => {
8253
+ delivered = deliveredOutcome(command, data);
8254
+ }
7331
8255
  };
8256
+ let served = false;
7332
8257
  let settled;
7333
8258
  try {
7334
8259
  settled = {
@@ -7336,24 +8261,35 @@ async function runCommand(routed, { base, signal, format: baseFormat }) {
7336
8261
  outcome: await control.settle(execute(command, input, ctx, {
7337
8262
  dryRun: flags.dryRun,
7338
8263
  confirm: flags.confirm,
8264
+ yes: flags.yes,
8265
+ force: flags.force,
7339
8266
  page: flags.page,
7340
8267
  invocation,
7341
- commandLine
8268
+ commandLine,
8269
+ started: async (outcome) => {
8270
+ writeStdout(await startedOutput(outcome, format, spill));
8271
+ served = true;
8272
+ }
7342
8273
  }))
7343
8274
  };
7344
8275
  } catch (error) {
8276
+ if (served && control.signal.aborted) return STOPPED_RESULT;
7345
8277
  settled = {
7346
8278
  ok: false,
7347
8279
  error: control.errorAfter(error),
7348
- render: renderOf(command)
8280
+ delivered
7349
8281
  };
7350
8282
  }
7351
8283
  return commandResult(settled, {
7352
8284
  ...format,
7353
8285
  fields: flags.fields
7354
- });
8286
+ }, spill);
8287
+ }
8288
+ function portsOf(cli, replacement) {
8289
+ if (replacement !== void 0) return () => replacement;
8290
+ return cli.ports ?? (() => ({}));
7355
8291
  }
7356
- async function runWithProgress(cli, argv, options, writeProgress) {
8292
+ async function runWithProgress(cli, argv, options, write) {
7357
8293
  const format = {
7358
8294
  cliName: cli.name,
7359
8295
  env: options.env,
@@ -7362,40 +8298,51 @@ async function runWithProgress(cli, argv, options, writeProgress) {
7362
8298
  cliName: cli.name,
7363
8299
  argv
7364
8300
  },
7365
- json: false
8301
+ json: false,
8302
+ errors: cli.errors ?? {},
8303
+ stop: options.signal
7366
8304
  };
8305
+ const builtIns = cli.commands === void 0 ? {} : builtInCommands(cli, options.bin);
7367
8306
  const root = cli.commands === void 0 ? commandSpec(cli.name, cli.command) : groupSpec(cli.name, {
7368
8307
  ...cli.commands,
7369
- ...builtInCommands(cli, options.bin)
8308
+ ...builtIns
7370
8309
  });
7371
8310
  const routed = route(root, format.invocation);
8311
+ const misused = (node) => isAgent(options.env) ? {
8312
+ ...format,
8313
+ usageHelp: textHelp(node, root, cli, format.style)
8314
+ } : format;
7372
8315
  switch (routed.kind) {
7373
- case "version": return versionResult(cli);
8316
+ case "version": return versionResult(cli, packageRootOf(options.bin, options.cwd));
7374
8317
  case "help": return helpResult(routed.node, root, cli, {
7375
8318
  ...format,
7376
8319
  json: routed.json
7377
8320
  });
7378
8321
  case "usage error": return errorResult(routed.error, {
7379
- ...format,
8322
+ ...misused(routed.node),
7380
8323
  json: routed.json
7381
8324
  });
7382
8325
  case "command": return runCommand(routed, {
7383
8326
  base: {
7384
8327
  cwd: options.cwd,
7385
8328
  env: options.env,
7386
- progress: (message) => writeProgress(`${message}\n`)
8329
+ packageRoot: packageRootOf(options.bin, options.cwd),
8330
+ progress: (message) => write.stderr(`${message}\n`),
8331
+ secret: (name) => secretOf(cli.name, options.env, name)
7387
8332
  },
8333
+ writeStdout: (text) => write.stdout(text),
7388
8334
  signal: options.signal,
7389
- format
8335
+ format: misused(routed.spec),
8336
+ ports: portsOf(cli, options.ports),
8337
+ clock: options.clock ?? realClock,
8338
+ files: options.files ?? realFiles(),
8339
+ spawn: options.spawn ?? realSpawn,
8340
+ projectFile: Object.values(builtIns).includes(routed.spec.command) ? void 0 : cli.project
7390
8341
  });
7391
8342
  }
7392
8343
  }
7393
8344
  //#endregion
7394
8345
  //#region ../agent-cli/dist/run-process.js
7395
- const SIGNAL_EXIT_CODES = {
7396
- SIGINT: 130,
7397
- SIGTERM: 143
7398
- };
7399
8346
  function written(stream, text) {
7400
8347
  return new Promise((resolve) => stream.write(text, () => resolve()));
7401
8348
  }
@@ -7404,7 +8351,7 @@ async function runProcess(cli) {
7404
8351
  const onSignal = (signal) => {
7405
8352
  if (stop.signal.aborted) process.exit(SIGNAL_EXIT_CODES[signal]);
7406
8353
  process.stderr.write("\nstopping… (Ctrl-C again to quit now)\n");
7407
- stop.abort();
8354
+ stop.abort(signal);
7408
8355
  };
7409
8356
  process.on("SIGINT", onSignal);
7410
8357
  process.on("SIGTERM", onSignal);
@@ -7415,7 +8362,10 @@ async function runProcess(cli) {
7415
8362
  stdoutIsTTY: process.stdout.isTTY === true,
7416
8363
  signal: stop.signal,
7417
8364
  bin: process.argv[1]
7418
- }, (line) => process.stderr.write(line));
8365
+ }, {
8366
+ stdout: (text) => process.stdout.write(text),
8367
+ stderr: (text) => process.stderr.write(text)
8368
+ });
7419
8369
  await written(process.stdout, result.stdout);
7420
8370
  await written(process.stderr, result.stderr);
7421
8371
  process.exitCode = result.exitCode;
@@ -7428,11 +8378,13 @@ async function runProcess(cli) {
7428
8378
  //#endregion
7429
8379
  //#region src/build.ts
7430
8380
  const build = {
7431
- "sha": "f35d0e3",
7432
- "dirty": false
8381
+ "sha": "411548a",
8382
+ "dirty": false,
8383
+ "builtAt": "2026-10-19T08:42:00.085Z"
7433
8384
  };
7434
8385
  //#endregion
7435
8386
  //#region src/steps.ts
8387
+ const stepErrors = defineErrors({ STEP_FAILED: { class: "operator" } });
7436
8388
  const INSTALL = [{
7437
8389
  command: "pnpm",
7438
8390
  args: ["install"]
@@ -7501,11 +8453,9 @@ function tail(output) {
7501
8453
  return output.trimEnd().split(/\r?\n/).slice(-20).join("\n");
7502
8454
  }
7503
8455
  function stepFailed(problem, left, cwd, cause) {
7504
- return new CliError({
7505
- code: "STEP_FAILED",
8456
+ return cliError(stepErrors, "STEP_FAILED", {
7506
8457
  message: problem,
7507
- hint: `fix the problem, then run in ${cwd}: ${left.map(stepLine).join(" && ")}`,
7508
- retryable: false
8458
+ hint: `fix the problem, then run in ${cwd}: ${left.map(stepLine).join(" && ")}`
7509
8459
  }, { cause });
7510
8460
  }
7511
8461
  /** Runs each step in `cwd` in order, capturing its output so only the Runtime writes to stdout. */
@@ -7559,9 +8509,10 @@ async function writeTemplate(root, files, values) {
7559
8509
  }
7560
8510
  //#endregion
7561
8511
  //#region src/version.ts
7562
- const version = "0.2.1";
8512
+ const version = "0.3.0";
7563
8513
  //#endregion
7564
8514
  //#region src/create.ts
8515
+ const { defineCommand } = agentCli();
7565
8516
  const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?([a-z0-9][a-z0-9._-]*)$/;
7566
8517
  function errorCode(error) {
7567
8518
  return error instanceof Error && "code" in error ? error.code : void 0;
@@ -7582,64 +8533,69 @@ async function ensureFree(path) {
7582
8533
  hint: "run `create-agent-cli <name>` with a name whose directory is free, or empty it first"
7583
8534
  });
7584
8535
  }
8536
+ const create = defineCommand({
8537
+ name: "create",
8538
+ description: "Create a new Agent CLI project in ./<name>",
8539
+ examples: [{
8540
+ command: "create-agent-cli weather --description \"Weather forecasts\"",
8541
+ note: "create ./weather, install dependencies and make the first commit"
8542
+ }],
8543
+ effect: "write",
8544
+ args: object({ name: string().regex(PACKAGE_NAME, "must be a lowercase npm package name, such as weather or @me/weather").describe("package name; its unscoped part names the bin and the directory") }),
8545
+ options: object({
8546
+ description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
8547
+ install: boolean().default(true).describe("run pnpm install"),
8548
+ git: boolean().default(true).describe("create a git repository with an initial commit")
8549
+ }),
8550
+ plan: async ({ name, description, install, git }, ctx) => {
8551
+ const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
8552
+ const path = resolve(ctx.cwd, binName);
8553
+ await ensureFree(path);
8554
+ const values = {
8555
+ packageName: name,
8556
+ binName,
8557
+ description,
8558
+ debugVariable: debugVariable(binName),
8559
+ runtimeVersion: version
8560
+ };
8561
+ return {
8562
+ path,
8563
+ values,
8564
+ files: await templateFiles(values),
8565
+ steps: stepsFor({
8566
+ install,
8567
+ git
8568
+ }),
8569
+ install
8570
+ };
8571
+ },
8572
+ apply: async (plan, ctx) => {
8573
+ await mkdir(plan.path, { recursive: true });
8574
+ await writeTemplate(plan.path, plan.files, plan.values);
8575
+ await runSteps(plan.steps, plan.path, ctx);
8576
+ return {
8577
+ path: plan.path,
8578
+ next: `cd ${plan.values.binName} && pnpm ${plan.install ? "test" : "install"}`
8579
+ };
8580
+ },
8581
+ render: ({ path, next }) => `Created ${path}\nnext: ${next}`,
8582
+ renderPlan: (plan) => [
8583
+ `Would create ${plan.path}:`,
8584
+ ...plan.files.map((file) => ` create ${file.target}`),
8585
+ ...plan.steps.map((step) => ` run ${stepLine(step)}`)
8586
+ ].join("\n")
8587
+ });
8588
+ //#endregion
8589
+ //#region src/cli.ts
8590
+ const { defineCli } = agentCli();
7585
8591
  //#endregion
7586
8592
  //#region src/bin.ts
7587
8593
  await runProcess(defineCli({
7588
8594
  name: "create-agent-cli",
7589
8595
  version,
7590
8596
  build,
7591
- command: defineCommand({
7592
- name: "create",
7593
- description: "Create a new Agent CLI project in ./<name>",
7594
- examples: [{
7595
- command: "create-agent-cli weather --description \"Weather forecasts\"",
7596
- note: "create ./weather, install dependencies and make the first commit"
7597
- }],
7598
- effect: "write",
7599
- args: object({ name: string().regex(PACKAGE_NAME, "must be a lowercase npm package name, such as weather or @me/weather").describe("package name; its unscoped part names the bin and the directory") }),
7600
- options: object({
7601
- description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
7602
- install: boolean().default(true).describe("run pnpm install"),
7603
- git: boolean().default(true).describe("create a git repository with an initial commit")
7604
- }),
7605
- plan: async ({ name, description, install, git }, ctx) => {
7606
- const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
7607
- const path = resolve(ctx.cwd, binName);
7608
- await ensureFree(path);
7609
- const values = {
7610
- packageName: name,
7611
- binName,
7612
- description,
7613
- debugVariable: debugVariable(binName),
7614
- runtimeVersion: version
7615
- };
7616
- return {
7617
- path,
7618
- values,
7619
- files: await templateFiles(values),
7620
- steps: stepsFor({
7621
- install,
7622
- git
7623
- }),
7624
- install
7625
- };
7626
- },
7627
- apply: async (plan, ctx) => {
7628
- await mkdir(plan.path, { recursive: true });
7629
- await writeTemplate(plan.path, plan.files, plan.values);
7630
- await runSteps(plan.steps, plan.path, ctx);
7631
- return {
7632
- path: plan.path,
7633
- next: `cd ${plan.values.binName} && pnpm ${plan.install ? "test" : "install"}`
7634
- };
7635
- },
7636
- render: ({ path, next }) => `Created ${path}\nnext: ${next}`,
7637
- renderPlan: (plan) => [
7638
- `Would create ${plan.path}:`,
7639
- ...plan.files.map((file) => ` create ${file.target}`),
7640
- ...plan.steps.map((step) => ` run ${stepLine(step)}`)
7641
- ].join("\n")
7642
- })
8597
+ errors: stepErrors,
8598
+ command: create
7643
8599
  }));
7644
8600
  //#endregion
7645
8601
  export {};