@duyquangnvx/create-agent-cli 0.2.1 → 0.4.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;
@@ -5537,21 +5603,29 @@ function describe(schema) {
5537
5603
  current = current.unwrap();
5538
5604
  description ??= current.description;
5539
5605
  }
5606
+ const variadic = current instanceof ZodArray;
5607
+ if (current instanceof ZodArray) current = current.element;
5540
5608
  return {
5541
5609
  type: typeOf(current),
5542
5610
  required,
5543
5611
  defaultValue,
5544
5612
  description,
5545
- choices: current instanceof ZodEnum ? current.options.map(String) : void 0
5613
+ choices: current instanceof ZodEnum ? current.options.map(String) : void 0,
5614
+ variadic
5546
5615
  };
5547
5616
  }
5617
+ function argLabel(key, { variadic, required }) {
5618
+ if (!variadic) return `<${key}>`;
5619
+ return required ? `<${key}...>` : `[${key}...]`;
5620
+ }
5548
5621
  function argField(key, schema) {
5549
- const label = `<${key}>`;
5622
+ const described = describe(schema);
5623
+ const label = argLabel(key, described);
5550
5624
  return {
5551
5625
  key,
5552
5626
  name: key,
5553
5627
  schema,
5554
- ...describe(schema),
5628
+ ...described,
5555
5629
  label,
5556
5630
  usage: label
5557
5631
  };
@@ -5571,42 +5645,52 @@ function optionField(key, schema) {
5571
5645
  usage
5572
5646
  };
5573
5647
  }
5574
- function fieldsOf(schema, toField) {
5648
+ function fieldsOf$1(schema, toField) {
5575
5649
  return schema === void 0 ? [] : Object.entries(schema.shape).map(([key, value]) => toField(key, value));
5576
5650
  }
5577
5651
  function argFields(schema) {
5578
- return fieldsOf(schema, argField);
5652
+ return fieldsOf$1(schema, argField);
5653
+ }
5654
+ /** Throws when an arg that takes every remaining operand is not the last arg. */
5655
+ function checkArrayArgLast(name, args) {
5656
+ const misplaced = argFields(args).slice(0, -1).filter((field) => field.variadic);
5657
+ if (misplaced.length > 0) {
5658
+ const keys = misplaced.map(({ key }) => `'${key}'`).join(", ");
5659
+ throw new Error(`Command '${name}' declares array arg ${keys} before its last arg; only the last arg may be an array`);
5660
+ }
5579
5661
  }
5580
5662
  function optionFields(schema) {
5581
- return fieldsOf(schema, optionField);
5663
+ return fieldsOf$1(schema, optionField);
5582
5664
  }
5583
5665
  //#endregion
5584
5666
  //#region ../agent-cli/dist/errors.js
5585
5667
  var CliError = class extends Error {
5586
5668
  code;
5587
5669
  hint;
5588
- retryable;
5589
- constructor({ code, message, hint, retryable }, options) {
5670
+ details;
5671
+ retryAt;
5672
+ constructor({ code, message, hint, details, retryAt }, options) {
5590
5673
  super(message, options);
5591
5674
  this.name = "CliError";
5592
5675
  this.code = code;
5593
5676
  this.hint = hint;
5594
- this.retryable = retryable;
5677
+ this.details = details;
5678
+ this.retryAt = retryAt;
5595
5679
  }
5596
5680
  };
5597
- /** The caller called the Agent CLI wrong; retrying the same call can't succeed. */
5681
+ /** The caller called the Agent CLI wrong; the same call can't succeed. */
5598
5682
  var UsageError = class extends CliError {
5599
- constructor(details) {
5600
- super({
5601
- ...details,
5602
- retryable: false
5603
- });
5683
+ constructor(fields) {
5684
+ super(fields);
5604
5685
  this.name = "UsageError";
5605
5686
  }
5606
5687
  };
5607
5688
  const INTERNAL_CODE = "INTERNAL";
5689
+ function errnoOf(error) {
5690
+ return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
5691
+ }
5608
5692
  function isMissingFile(error) {
5609
- return error instanceof Error && "code" in error && error.code === "ENOENT";
5693
+ return errnoOf(error) === "ENOENT";
5610
5694
  }
5611
5695
  function debugVariable(cliName) {
5612
5696
  return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_DEBUG`;
@@ -5617,47 +5701,61 @@ function messageOf(error) {
5617
5701
  function isDebug({ env, cliName }) {
5618
5702
  return Boolean(env[debugVariable(cliName)]);
5619
5703
  }
5620
- function detailsOf(error, format) {
5704
+ function bugHint(format) {
5705
+ return isDebug(format) ? "the stack trace shows where it failed" : `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
5706
+ }
5707
+ function fieldsOf(error, format) {
5621
5708
  if (error instanceof CliError) {
5622
- const { code, message, hint, retryable } = error;
5709
+ const { code, message, hint, details, retryAt } = error;
5623
5710
  return {
5624
5711
  code,
5625
5712
  message,
5626
- hint,
5627
- retryable
5713
+ hint: format.isBug(error) ? bugHint(format) : hint,
5714
+ details,
5715
+ retryAt
5628
5716
  };
5629
5717
  }
5630
5718
  return {
5631
5719
  code: INTERNAL_CODE,
5632
5720
  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
5721
+ hint: bugHint(format)
5635
5722
  };
5636
5723
  }
5637
- function stackOf(error, format) {
5638
- return isDebug(format) && !(error instanceof CliError) && error instanceof Error ? error.stack : void 0;
5724
+ function causesOf(error) {
5725
+ return error instanceof Error && error.cause !== void 0 ? [error.cause, ...causesOf(error.cause)] : [];
5639
5726
  }
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`;
5727
+ function traceOf(error) {
5728
+ return error instanceof Error ? error.stack ?? error.message : String(error);
5729
+ }
5730
+ /** Under debug: a bug's stack trace, then every error's cause chain. */
5731
+ function debugTrace(error, format) {
5732
+ if (!isDebug(format)) return void 0;
5733
+ const lines = [...format.isBug(error) ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
5734
+ return lines.length === 0 ? void 0 : lines.join("\n");
5735
+ }
5736
+ /** The errors a run failed with: each one an `AggregateError` holds, or the one it threw. */
5737
+ function errorsOf(error) {
5738
+ return error instanceof AggregateError && error.errors.length > 0 ? error.errors : [error];
5739
+ }
5740
+ function errorLines(error, format) {
5741
+ const fields = fieldsOf(error, format);
5742
+ const stack = debugTrace(error, format);
5647
5743
  const { style } = format;
5648
- const label = style.red(error instanceof CliError ? "error:" : "internal error:");
5744
+ const label = style.red(format.isBug(error) ? "internal error:" : "error:");
5649
5745
  const trace = stack === void 0 ? "" : `${stack}\n`;
5650
- return `${label} ${details.message}\n${trace}${style.cyan("hint:")} ${details.hint}\n`;
5746
+ return `${label} ${fields.message}\n${trace}${style.cyan("hint:")} ${fields.hint}\n`;
5651
5747
  }
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;
5748
+ function formatRuntimeError(error, format) {
5749
+ const errors = errorsOf(error);
5750
+ if (format.json) {
5751
+ const printed = errors.map((each) => ({
5752
+ ...fieldsOf(each, format),
5753
+ stack: debugTrace(each, format)
5754
+ }));
5755
+ return `${JSON.stringify({ errors: printed })}\n`;
5659
5756
  }
5660
- };
5757
+ return errors.map((each) => errorLines(each, format)).join("");
5758
+ }
5661
5759
  const SHELL_SAFE = /^[\w@%+=:,./-]+$/;
5662
5760
  function quoted$1(token) {
5663
5761
  return SHELL_SAFE.test(token) ? token : `'${token.replaceAll("'", `'\\''`)}'`;
@@ -5665,6 +5763,10 @@ function quoted$1(token) {
5665
5763
  function pasteable(cliName, tokens) {
5666
5764
  return [cliName, ...tokens].map(quoted$1).join(" ");
5667
5765
  }
5766
+ /** The caller's command line as typed, ready to paste. */
5767
+ function commandLineOf({ cliName, argv }) {
5768
+ return pasteable(cliName, argv);
5769
+ }
5668
5770
  function split(argv) {
5669
5771
  const end = argv.indexOf("--");
5670
5772
  return end === -1 ? {
@@ -5678,13 +5780,16 @@ function split(argv) {
5678
5780
  function without(flag, tokens) {
5679
5781
  return tokens.filter((token, index) => token !== flag && !token.startsWith(`${flag}=`) && tokens[index - 1] !== flag);
5680
5782
  }
5681
- /** The caller's command line, ready to paste, with `flag value` replacing any value they gave. */
5783
+ /**
5784
+ * The caller's command line, ready to paste, with `flag value` (or `flag` alone, for a flag that
5785
+ * takes nothing) replacing any value they gave.
5786
+ */
5682
5787
  function commandLineWith({ cliName, argv }, flag, value) {
5683
5788
  const { options, operands } = split(argv);
5789
+ const added = value === void 0 ? [flag] : [flag, value];
5684
5790
  return pasteable(cliName, [
5685
5791
  ...without(flag, options),
5686
- flag,
5687
- value,
5792
+ ...added,
5688
5793
  ...operands
5689
5794
  ]);
5690
5795
  }
@@ -5700,7 +5805,45 @@ function commandLineRenaming({ cliName, argv }, renames) {
5700
5805
  return pasteable(cliName, [...options.map((token) => renamed(token, renames)), ...operands]);
5701
5806
  }
5702
5807
  //#endregion
5808
+ //#region ../agent-cli/dist/usage.js
5809
+ const INVALID_INPUT = "INVALID_INPUT";
5810
+ const UNKNOWN_OPTION = "UNKNOWN_OPTION";
5811
+ const UNKNOWN_COMMAND = "UNKNOWN_COMMAND";
5812
+ function unknownError({ code, problem, commandLine, corrections, listed }) {
5813
+ return new UsageError({
5814
+ code,
5815
+ message: `${problem} for \`${commandLine}\``,
5816
+ hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
5817
+ });
5818
+ }
5819
+ function unknownOptionError(commandLine, invocation, flag, suggestions) {
5820
+ return unknownError({
5821
+ code: UNKNOWN_OPTION,
5822
+ problem: `unknown option '${flag}'`,
5823
+ commandLine,
5824
+ corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
5825
+ listed: "options"
5826
+ });
5827
+ }
5828
+ function unknownCommandError(commandLine, name, suggestions) {
5829
+ return unknownError({
5830
+ code: UNKNOWN_COMMAND,
5831
+ problem: `unknown command '${name}'`,
5832
+ commandLine,
5833
+ corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
5834
+ listed: "commands"
5835
+ });
5836
+ }
5837
+ function invalidInputError(commandLine, problem) {
5838
+ return new UsageError({
5839
+ code: INVALID_INPUT,
5840
+ message: problem,
5841
+ hint: `run \`${commandLine} --help\` to see the expected input`
5842
+ });
5843
+ }
5844
+ //#endregion
5703
5845
  //#region ../agent-cli/dist/list.js
5846
+ const UNKNOWN_FIELD = "UNKNOWN_FIELD";
5704
5847
  const DEFAULT_PAGE_LIMIT = 20;
5705
5848
  const FIELDS_FLAG = "--fields";
5706
5849
  const SEPARATOR = ",";
@@ -5770,13 +5913,13 @@ function selectedFields(values, rowFields, commandLine) {
5770
5913
  const hint = `pass ${FIELDS_FLAG} with any of: ${rowFields.join(", ")}`;
5771
5914
  const names = typeof raw === "string" ? raw.split(SEPARATOR).filter((name) => name !== "") : [];
5772
5915
  if (names.length === 0) throw new UsageError({
5773
- code: "INVALID_INPUT",
5916
+ code: INVALID_INPUT,
5774
5917
  message: `${FIELDS_FLAG}: missing value for \`${commandLine}\``,
5775
5918
  hint
5776
5919
  });
5777
5920
  const unknown = names.filter((name) => !rowFields.includes(name));
5778
5921
  if (unknown.length > 0) throw new UsageError({
5779
- code: "UNKNOWN_FIELD",
5922
+ code: UNKNOWN_FIELD,
5780
5923
  message: `unknown field ${quoted(unknown)} for \`${commandLine}\``,
5781
5924
  hint
5782
5925
  });
@@ -5808,6 +5951,12 @@ function pageFooter({ items, next }) {
5808
5951
  return `${items.length} shown; ${more}\n`;
5809
5952
  }
5810
5953
  //#endregion
5954
+ //#region ../agent-cli/dist/planned.js
5955
+ /** Whether `command` changes something through a Plan: a write or destructive Command. */
5956
+ function hasPlan(command) {
5957
+ return command.effect === "write" || command.effect === "destructive";
5958
+ }
5959
+ //#endregion
5811
5960
  //#region ../agent-cli/dist/flags.js
5812
5961
  const HELP_FLAG = "--help";
5813
5962
  const VERSION_FLAG = "--version";
@@ -5842,6 +5991,16 @@ const CONFIRM_FLAG = standardFlag("--confirm", {
5842
5991
  takes: "value",
5843
5992
  schema: string()
5844
5993
  });
5994
+ const YES_FLAG = standardFlag("--yes", {
5995
+ description: "approve the cost the run states; --dry-run shows it",
5996
+ takes: "nothing",
5997
+ schema: boolean()
5998
+ });
5999
+ const FORCE_FLAG = standardFlag("--force", {
6000
+ description: "replace the files the run writes when they exist; --dry-run lists them",
6001
+ takes: "nothing",
6002
+ schema: boolean()
6003
+ });
5845
6004
  const EFFECT_FLAGS = {
5846
6005
  read: [JSON_FLAG],
5847
6006
  write: [JSON_FLAG, DRY_RUN_FLAG],
@@ -5849,16 +6008,28 @@ const EFFECT_FLAGS = {
5849
6008
  JSON_FLAG,
5850
6009
  DRY_RUN_FLAG,
5851
6010
  CONFIRM_FLAG
5852
- ]
6011
+ ],
6012
+ serve: [JSON_FLAG]
5853
6013
  };
5854
6014
  const RESERVED = new Map([
5855
6015
  ...ROUTING_FLAGS,
5856
6016
  ...EFFECT_FLAGS.destructive.map(({ flag }) => flag),
6017
+ YES_FLAG.flag,
6018
+ FORCE_FLAG.flag,
5857
6019
  ...LIST_FLAGS
5858
6020
  ].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 ?? []];
6021
+ /** The Standard flags of a Command with this Effect and declarations, and of a List Command. */
6022
+ function standardFlagsFor(effect, { outputs, force, cost }, list) {
6023
+ const forceFlag = force === void 0 ? FORCE_FLAG : {
6024
+ ...FORCE_FLAG,
6025
+ description: force
6026
+ };
6027
+ return [
6028
+ ...EFFECT_FLAGS[effect],
6029
+ ...outputs || force !== void 0 ? [forceFlag] : [],
6030
+ ...cost ? [YES_FLAG] : [],
6031
+ ...list?.standardFlags ?? []
6032
+ ];
5862
6033
  }
5863
6034
  /** Throws when the Command declares an arg or option under a Standard flag's name. */
5864
6035
  function checkNoStandardFlags(command) {
@@ -5874,12 +6045,25 @@ function checkNoStandardFlags(command) {
5874
6045
  });
5875
6046
  if (lines.length > 0) throw new Error([`Command '${command.name}' declares Standard flags the Runtime adds; rename them:`, ...lines].join("\n"));
5876
6047
  }
6048
+ /** Throws when `force` is declared on a Command with no Plan, or says nothing. */
6049
+ function checkForce(command) {
6050
+ const force = "force" in command ? command.force : void 0;
6051
+ if (force === void 0) return;
6052
+ if (!hasPlan(command)) throw new Error(`Command '${command.name}' declares force, which only a write or destructive Command may`);
6053
+ if (force.trim() === "") throw new Error(`Command '${command.name}' declares an empty force; say what ${FORCE_FLAG.flag} replaces`);
6054
+ }
5877
6055
  //#endregion
5878
6056
  //#region ../agent-cli/dist/define.js
5879
- function defineCommand(command) {
6057
+ function checkedCommand(command) {
5880
6058
  checkNoStandardFlags(command);
6059
+ checkForce(command);
6060
+ checkArrayArgLast(command.name, command.args);
5881
6061
  return command;
5882
6062
  }
6063
+ function defineCommandFor() {
6064
+ return checkedCommand;
6065
+ }
6066
+ const defineCommand$1 = defineCommandFor();
5883
6067
  function isCommand(node) {
5884
6068
  return typeof node.effect === "string";
5885
6069
  }
@@ -5945,7 +6129,18 @@ function meetsMinimum(version, minimum) {
5945
6129
  return true;
5946
6130
  }
5947
6131
  //#endregion
6132
+ //#region ../agent-cli/dist/tail.js
6133
+ /** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
6134
+ function lastLines(output, count) {
6135
+ return output.trim().split("\n").slice(-count).join("\n");
6136
+ }
6137
+ //#endregion
5948
6138
  //#region ../agent-cli/dist/npm.js
6139
+ const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
6140
+ const REGISTRY_AUTH = "REGISTRY_AUTH";
6141
+ const INSTALL_FAILED = "INSTALL_FAILED";
6142
+ const NPM_FAILED = "NPM_FAILED";
6143
+ const NO_LATEST = "NO_LATEST";
5949
6144
  function npmContext(cli, install, ctx) {
5950
6145
  return {
5951
6146
  cliName: cli.name,
@@ -5955,6 +6150,7 @@ function npmContext(cli, install, ctx) {
5955
6150
  };
5956
6151
  }
5957
6152
  const NPMJS = "https://registry.npmjs.org/";
6153
+ const STDERR_TAIL_LINES$1 = 10;
5958
6154
  const RM_RETRIES = 3;
5959
6155
  const NETWORK_CODES = /* @__PURE__ */ new Set([
5960
6156
  "ENOTFOUND",
@@ -5986,7 +6182,7 @@ function installCommandLine(install, version = "latest") {
5986
6182
  }
5987
6183
  function spawnNpm(args, ctx, signal) {
5988
6184
  const windows = process.platform === "win32";
5989
- const command = windows ? ["npm", ...args].map((arg) => `"${arg.replaceAll("\"", "\"\"")}"`).join(" ") : "npm";
6185
+ const command = windows ? ["npm", ...args.map((arg) => `"${arg.replaceAll("\"", "\"\"")}"`)].join(" ") : "npm";
5990
6186
  return new Promise((resolve, reject) => {
5991
6187
  const child = spawn(command, windows ? [] : args, {
5992
6188
  env: ctx.env,
@@ -6017,45 +6213,40 @@ function spawnNpm(args, ctx, signal) {
6017
6213
  });
6018
6214
  }
6019
6215
  function tail$1(stderr) {
6020
- const lines = stderr.trim().split("\n").slice(-10);
6021
- return lines.length === 0 || lines[0] === "" ? "" : `\n${lines.join("\n")}`;
6216
+ const lines = lastLines(stderr, STDERR_TAIL_LINES$1);
6217
+ return lines === "" ? "" : `\n${lines}`;
6022
6218
  }
6023
6219
  function failureOf(call, answer, ctx) {
6024
6220
  const { registry, package: name } = ctx.install;
6025
6221
  const code = NPM_CODE.exec(answer.stderr)?.[1];
6026
6222
  const output = tail$1(answer.stderr);
6027
6223
  if (code !== void 0 && NETWORK_CODES.has(code)) return new CliError({
6028
- code: "REGISTRY_UNREACHABLE",
6224
+ code: REGISTRY_UNREACHABLE,
6029
6225
  message: `npm could not reach ${registry} (${code})${output}`,
6030
- hint: "check the network or VPN, then run it again",
6031
- retryable: true
6226
+ hint: "check the network or VPN, then run it again"
6032
6227
  });
6033
6228
  if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
6034
- code: "REGISTRY_AUTH",
6229
+ code: REGISTRY_AUTH,
6035
6230
  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
6231
+ hint: `log in to ${registry} (npm login --registry=${registry}) or set its token in ~/.npmrc, then run it again`
6038
6232
  });
6039
6233
  if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
6040
6234
  if (call === "install") return new CliError({
6041
- code: "INSTALL_FAILED",
6235
+ code: INSTALL_FAILED,
6042
6236
  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
6237
+ hint: `fix what npm reports, then run it again, or install it yourself: ${installCommandLine(ctx.install)}`
6045
6238
  });
6046
6239
  return new CliError({
6047
- code: "NPM_FAILED",
6240
+ code: NPM_FAILED,
6048
6241
  message: `npm ${call} failed with exit code ${answer.exitCode}${output}`,
6049
- hint: "fix what npm reports, then run it again",
6050
- retryable: false
6242
+ hint: "fix what npm reports, then run it again"
6051
6243
  });
6052
6244
  }
6053
6245
  function noLatest(ctx, message) {
6054
6246
  return new CliError({
6055
- code: "NO_LATEST",
6247
+ code: NO_LATEST,
6056
6248
  message,
6057
- hint: `check that ${ctx.install.package} is published on ${ctx.install.registry}`,
6058
- retryable: false
6249
+ hint: `check that ${ctx.install.package} is published on ${ctx.install.registry}`
6059
6250
  });
6060
6251
  }
6061
6252
  /**
@@ -6072,10 +6263,9 @@ async function runNpm(call, args, ctx, signal = ctx.signal) {
6072
6263
  ], ctx, signal).catch((error) => {
6073
6264
  if (signal?.aborted === true) throw signal.reason;
6074
6265
  throw new CliError({
6075
- code: "NPM_FAILED",
6266
+ code: NPM_FAILED,
6076
6267
  message: `npm could not be started: ${messageOf(error)}`,
6077
- hint: "install Node with npm, or put npm on PATH",
6078
- retryable: false
6268
+ hint: "install Node with npm, or put npm on PATH"
6079
6269
  }, { cause: error });
6080
6270
  });
6081
6271
  if (answer.exitCode !== 0) throw failureOf(call, answer, ctx);
@@ -6126,16 +6316,16 @@ async function latestVersion(ctx) {
6126
6316
  }
6127
6317
  //#endregion
6128
6318
  //#region ../agent-cli/dist/global-install.js
6319
+ const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
6129
6320
  /** Where npm puts global packages under its global prefix. */
6130
6321
  function globalModulesDir(prefix) {
6131
6322
  return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
6132
6323
  }
6133
6324
  function notGlobal(ctx, message) {
6134
6325
  return new CliError({
6135
- code: "NOT_GLOBAL_INSTALL",
6326
+ code: NOT_GLOBAL_INSTALL,
6136
6327
  message,
6137
- hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.install)}`,
6138
- retryable: false
6328
+ hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.install)}`
6139
6329
  });
6140
6330
  }
6141
6331
  async function realpathOrUndefined(path) {
@@ -6166,122 +6356,943 @@ async function globalInstallOf(bin, ctx) {
6166
6356
  };
6167
6357
  }
6168
6358
  //#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}`;
6359
+ //#region ../agent-cli/dist/confirm.js
6360
+ const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
6361
+ const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
6362
+ function checkConfirmation({ invocation, commandLine, given }, expected) {
6363
+ if (given === expected) return;
6364
+ const hint = `to confirm, run \`${commandLineWith(invocation, CONFIRM_FLAG.flag, expected)}\``;
6365
+ if (given === void 0) throw new UsageError({
6366
+ code: CONFIRMATION_REQUIRED,
6367
+ message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
6368
+ hint
6369
+ });
6370
+ throw new UsageError({
6371
+ code: CONFIRMATION_MISMATCH,
6372
+ message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
6373
+ hint
6374
+ });
6174
6375
  }
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];
6376
+ //#endregion
6377
+ //#region ../agent-cli/dist/cost.js
6378
+ const NOT_CONFIRMED = "NOT_CONFIRMED";
6379
+ /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
6380
+ function checkCost({ invocation, commandLine, yes }, cost) {
6381
+ if (cost === void 0 || yes) return;
6382
+ throw new CliError({
6383
+ code: NOT_CONFIRMED,
6384
+ message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
6385
+ hint: `to approve the cost, run \`${commandLineWith(invocation, YES_FLAG.flag)}\``
6386
+ });
6181
6387
  }
6182
- function homeOf(env) {
6183
- return env.HOME ?? env.USERPROFILE ?? homedir();
6388
+ //#endregion
6389
+ //#region ../agent-cli/dist/agent.js
6390
+ const AGENT_VARIABLES = [
6391
+ "CLAUDECODE",
6392
+ "CODEX_CI",
6393
+ "CODEX_SANDBOX",
6394
+ "GEMINI_CLI",
6395
+ "CURSOR_AGENT",
6396
+ "CI"
6397
+ ];
6398
+ /** Whether a Coding agent, not a person, is running the Agent CLI. */
6399
+ function isAgent(env) {
6400
+ return AGENT_VARIABLES.some((name) => env[name] !== void 0);
6184
6401
  }
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
- };
6402
+ //#endregion
6403
+ //#region ../agent-cli/dist/errno.js
6404
+ const PERMISSION_DENIED = "PERMISSION_DENIED";
6405
+ const DISK_FULL = "DISK_FULL";
6406
+ const NOT_FOUND = "NOT_FOUND";
6407
+ const ALREADY_EXISTS = "ALREADY_EXISTS";
6408
+ function permissionHint({ path, access, callerLine, env }) {
6409
+ if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
6410
+ return `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`;
6411
+ }
6412
+ /** The error a file system failure on `path` stands for; any other error comes back unchanged. */
6413
+ function classifyErrno(error, context) {
6414
+ const { path } = context;
6415
+ switch (errnoOf(error)) {
6416
+ case "EACCES":
6417
+ case "EPERM": return new CliError({
6418
+ code: PERMISSION_DENIED,
6419
+ message: `permission denied: ${path}`,
6420
+ hint: permissionHint(context)
6421
+ }, { cause: error });
6422
+ case "ENOSPC": return new CliError({
6423
+ code: DISK_FULL,
6424
+ message: `no space left on the disk holding ${path}`,
6425
+ hint: "free space on that disk, then run the same command again"
6426
+ }, { cause: error });
6427
+ case "ENOENT": return new CliError({
6428
+ code: NOT_FOUND,
6429
+ message: `not found: ${path}`,
6430
+ hint: `check that ${path} and its folder exist`
6431
+ }, { cause: error });
6432
+ case "EEXIST": return new CliError({
6433
+ code: ALREADY_EXISTS,
6434
+ message: `already exists: ${path}`,
6435
+ hint: `pass another path, or remove ${path}`
6436
+ }, { cause: error });
6437
+ default: return error;
6196
6438
  }
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
6439
  }
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);
6440
+ //#endregion
6441
+ //#region ../agent-cli/dist/clock.js
6442
+ const realClock = {
6443
+ now: () => Date.now(),
6444
+ sleep: (ms, signal) => new Promise((resolve, reject) => {
6445
+ if (signal?.aborted === true) {
6446
+ reject(signal.reason);
6447
+ return;
6220
6448
  }
6449
+ const onAbort = () => {
6450
+ clearTimeout(timer);
6451
+ reject(signal?.reason);
6452
+ };
6453
+ const timer = setTimeout(() => {
6454
+ signal?.removeEventListener("abort", onAbort);
6455
+ resolve();
6456
+ }, ms);
6457
+ signal?.addEventListener("abort", onAbort, { once: true });
6458
+ })
6459
+ };
6460
+ /** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
6461
+ function after(clock, ms, cancel, then) {
6462
+ clock.sleep(ms, cancel).then(then, (error) => {
6463
+ if (!cancel.aborted) throw error;
6464
+ });
6465
+ }
6466
+ async function poll(clock, check, intervalMs, signal) {
6467
+ for (;;) {
6468
+ signal.throwIfAborted();
6469
+ const value = await check();
6470
+ if (value !== void 0) return value;
6471
+ await clock.sleep(intervalMs, signal);
6221
6472
  }
6222
- return copies;
6223
6473
  }
6224
6474
  //#endregion
6225
- //#region ../agent-cli/dist/doctor-rows.js
6226
- function ok(message) {
6227
- return {
6228
- state: "ok",
6229
- message
6230
- };
6475
+ //#region ../agent-cli/dist/interrupts.js
6476
+ const INTERRUPTED = "INTERRUPTED";
6477
+ const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
6478
+ /** How long an interrupted Command may take to settle before the run ends without it. */
6479
+ const GRACE_MS = 3e3;
6480
+ const GRACE_OVER = Symbol("grace over");
6481
+ function interruptedError() {
6482
+ return new CliError({
6483
+ code: INTERRUPTED,
6484
+ message: "interrupted before it finished",
6485
+ hint: "run the command again"
6486
+ });
6231
6487
  }
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
- };
6488
+ const WAIT_HINT = "run it again later, when the service it waits on may answer faster";
6489
+ function deadlineError(commandLine, ms, hint) {
6490
+ return new CliError({
6491
+ code: DEADLINE_EXCEEDED,
6492
+ message: `${commandLine} ran past its ${ms} ms deadline`,
6493
+ hint
6494
+ });
6253
6495
  }
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
- }
6496
+ /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
6497
+ var RunControl = class {
6498
+ signal;
6499
+ #controller = new AbortController();
6500
+ #commandLine;
6501
+ #outer;
6502
+ #onOuterAbort = () => this.#controller.abort(interruptedError());
6503
+ /** Each running must-finish step, tracked until it settles, with its name. */
6504
+ #steps = /* @__PURE__ */ new Map();
6505
+ #progress;
6506
+ #clock;
6507
+ #disposed = new AbortController();
6508
+ #deadlineHit;
6509
+ #graceCancel = new AbortController();
6510
+ #endGrace = () => {};
6511
+ #graceOver = new Promise((resolve) => {
6512
+ this.#endGrace = resolve;
6513
+ });
6514
+ constructor({ commandLine, outer, clock, progress, runDeadline }) {
6515
+ this.#commandLine = commandLine;
6516
+ this.#outer = outer;
6517
+ this.#clock = clock;
6518
+ this.#progress = progress;
6519
+ this.signal = this.#controller.signal;
6520
+ this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
6521
+ if (outer?.aborted === true) this.#onOuterAbort();
6522
+ else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
6523
+ if (runDeadline !== void 0) {
6524
+ const { ms, hint } = runDeadline;
6525
+ after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, hint)));
6270
6526
  }
6271
- };
6272
- }
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`,
6527
+ }
6528
+ deadline(ms) {
6529
+ const controller = new AbortController();
6530
+ after(this.#clock, ms, this.#disposed.signal, () => {
6531
+ this.#deadlineHit ??= deadlineError(this.#commandLine, ms, WAIT_HINT);
6532
+ controller.abort(this.#deadlineHit);
6533
+ });
6534
+ return AbortSignal.any([this.signal, controller.signal]);
6535
+ }
6536
+ async runToEnd(name, work) {
6537
+ this.#graceCancel.abort();
6538
+ if (this.signal.aborted) this.#finishing(name);
6539
+ const step = work();
6540
+ const settled = step.then(() => {}, () => {});
6541
+ this.#steps.set(settled, name);
6542
+ try {
6543
+ return await step;
6544
+ } finally {
6545
+ this.#steps.delete(settled);
6546
+ this.#startGrace();
6547
+ }
6548
+ }
6549
+ /** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
6550
+ async settle(work) {
6551
+ try {
6552
+ const value = await Promise.race([work, this.#graceOver]);
6553
+ if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
6554
+ return value;
6555
+ } finally {
6556
+ await this.#stepsDone();
6557
+ this.#dispose();
6558
+ }
6559
+ }
6560
+ /**
6561
+ * The error a run ends with: an interrupt or a missed deadline outranks what the Command threw,
6562
+ * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
6563
+ */
6564
+ errorAfter(error) {
6565
+ if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
6566
+ if (this.#deadlineHit !== void 0 && !(error instanceof CliError)) return this.#deadlineHit;
6567
+ return error;
6568
+ }
6569
+ #onStop() {
6570
+ for (const name of this.#steps.values()) this.#finishing(name);
6571
+ this.#startGrace();
6572
+ }
6573
+ #finishing(name) {
6574
+ this.#progress(`finishing ${name}`);
6575
+ }
6576
+ #startGrace() {
6577
+ if (this.signal.aborted && this.#steps.size === 0) {
6578
+ this.#graceCancel.abort();
6579
+ this.#graceCancel = new AbortController();
6580
+ after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
6581
+ }
6582
+ }
6583
+ async #stepsDone() {
6584
+ while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
6585
+ }
6586
+ #dispose() {
6587
+ this.#graceCancel.abort();
6588
+ this.#disposed.abort();
6589
+ this.#outer?.removeEventListener("abort", this.#onOuterAbort);
6590
+ }
6591
+ };
6592
+ //#endregion
6593
+ //#region ../agent-cli/dist/outputs.js
6594
+ const OUTPUT_EXISTS = "OUTPUT_EXISTS";
6595
+ /** Refuses a run that would overwrite existing outputs unless the caller passed `--force`. */
6596
+ async function checkOutputs({ invocation, commandLine, force, files }, outputs) {
6597
+ if (outputs === void 0 || force) return;
6598
+ const found = await Promise.all(outputs.map((path) => files.exists(path)));
6599
+ const existing = outputs.filter((_path, index) => found[index]);
6600
+ if (existing.length === 0) return;
6601
+ const them = existing.length === 1 ? "it" : "them";
6602
+ throw new CliError({
6603
+ code: OUTPUT_EXISTS,
6604
+ message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
6605
+ hint: `to replace ${them}, run \`${commandLineWith(invocation, FORCE_FLAG.flag)}\``
6606
+ });
6607
+ }
6608
+ //#endregion
6609
+ //#region ../agent-cli/dist/project.js
6610
+ const PROJECT_INVALID = "PROJECT_INVALID";
6611
+ function invalid(path, problem, options) {
6612
+ return new CliError({
6613
+ code: PROJECT_INVALID,
6614
+ message: `the Project file ${path} is invalid: ${problem}`,
6615
+ hint: `fix ${path}, then run the command again`
6616
+ }, options);
6617
+ }
6618
+ function readText(path) {
6619
+ try {
6620
+ return readFileSync(path, "utf8");
6621
+ } catch (error) {
6622
+ if (isMissingFile(error)) return void 0;
6623
+ throw error;
6624
+ }
6625
+ }
6626
+ function parsed(path, text, schema) {
6627
+ let json;
6628
+ try {
6629
+ json = JSON.parse(text);
6630
+ } catch (error) {
6631
+ throw invalid(path, messageOf(error), { cause: error });
6632
+ }
6633
+ const result = schema.safeParse(json);
6634
+ if (!result.success) throw invalid(path, prettifyError(result.error));
6635
+ return result.data;
6636
+ }
6637
+ function projectFrom({ file, schema }, folder) {
6638
+ const path = join(folder, file);
6639
+ const text = readText(path);
6640
+ if (text !== void 0) return {
6641
+ root: folder,
6642
+ settings: parsed(path, text, schema)
6643
+ };
6644
+ const parent = dirname(folder);
6645
+ return parent === folder ? void 0 : projectFrom({
6646
+ file,
6647
+ schema
6648
+ }, parent);
6649
+ }
6650
+ /** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
6651
+ function projectOf(projectFile, cwd) {
6652
+ return projectFrom(projectFile, resolve(cwd));
6653
+ }
6654
+ //#endregion
6655
+ //#region ../agent-cli/dist/tools.js
6656
+ const TOOL_MISSING = "TOOL_MISSING";
6657
+ const WINDOWS_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
6658
+ const NOT_EXECUTABLE = /* @__PURE__ */ new Set([
6659
+ "ENOENT",
6660
+ "ENOTDIR",
6661
+ "EACCES"
6662
+ ]);
6663
+ function pathOf(env) {
6664
+ return env[Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? "PATH"] ?? "";
6665
+ }
6666
+ function extensionsOf(env) {
6667
+ return process.platform === "win32" ? (env.PATHEXT ?? WINDOWS_EXTENSIONS).split(";") : [""];
6668
+ }
6669
+ async function isExecutable(file) {
6670
+ try {
6671
+ await access(file, constants$1.X_OK);
6672
+ return true;
6673
+ } catch (error) {
6674
+ if (NOT_EXECUTABLE.has(errnoOf(error) ?? "")) return false;
6675
+ throw error;
6676
+ }
6677
+ }
6678
+ /** Where `name` is found on `PATH` from `env`, or undefined. */
6679
+ async function findTool(name, env) {
6680
+ const candidates = pathOf(env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => extensionsOf(env).map((extension) => join(folder, `${name}${extension}`)));
6681
+ for (const file of candidates) if (await isExecutable(file)) return file;
6682
+ }
6683
+ /** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
6684
+ async function checkTools(tools, env) {
6685
+ const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
6686
+ const missing = tools.filter((_tool, index) => found[index] === void 0);
6687
+ if (missing.length === 0) return;
6688
+ const names = missing.map(({ name }) => name).join(", ");
6689
+ throw new CliError({
6690
+ code: TOOL_MISSING,
6691
+ message: `${missing.length === 1 ? "a tool it runs is" : "tools it runs are"} not on PATH: ${names}`,
6692
+ hint: `install ${missing.length === 1 ? "it" : "them"}, then run the same command again: ${missing.map(({ install }) => install).join("; ")}`
6693
+ });
6694
+ }
6695
+ function toolsIn(node) {
6696
+ return isCommand(node) ? [...node.tools ?? []] : Object.values(node).flatMap(toolsIn);
6697
+ }
6698
+ /** One `doctor` row per distinct tool the Commands declare, with its install command as the fix. */
6699
+ function toolRows(commands) {
6700
+ return [...new Map(toolsIn(commands).map((tool) => [tool.name, tool])).values()].map(({ name, install }) => ({
6701
+ name,
6702
+ run: async (ctx) => {
6703
+ const file = await findTool(name, ctx.env);
6704
+ return file === void 0 ? {
6705
+ state: "problem",
6706
+ message: `${name} is not on PATH`,
6707
+ fix: install
6708
+ } : {
6709
+ state: "ok",
6710
+ message: `found at ${file}`
6711
+ };
6712
+ }
6713
+ }));
6714
+ }
6715
+ //#endregion
6716
+ //#region ../agent-cli/dist/spawn.js
6717
+ const TOOL_FAILED = "TOOL_FAILED";
6718
+ const STDERR_TAIL_LINES = 20;
6719
+ const SIGNAL_EXIT_BASE = 128;
6720
+ const realSpawn = (command, args, { cwd, env, signal }) => new Promise((resolve, reject) => {
6721
+ signal.throwIfAborted();
6722
+ const child = spawn(command, args, {
6723
+ cwd,
6724
+ env,
6725
+ stdio: [
6726
+ "ignore",
6727
+ "pipe",
6728
+ "pipe"
6729
+ ]
6730
+ });
6731
+ const stdout = [];
6732
+ const stderr = [];
6733
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
6734
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
6735
+ const kill = () => {
6736
+ child.kill();
6737
+ };
6738
+ signal.addEventListener("abort", kill, { once: true });
6739
+ child.on("error", (error) => {
6740
+ signal.removeEventListener("abort", kill);
6741
+ reject(error);
6742
+ });
6743
+ child.on("close", (code, killedBy) => {
6744
+ signal.removeEventListener("abort", kill);
6745
+ if (signal.aborted) {
6746
+ reject(signal.reason);
6747
+ return;
6748
+ }
6749
+ resolve({
6750
+ stdout: Buffer.concat(stdout).toString(),
6751
+ stderr: Buffer.concat(stderr).toString(),
6752
+ exitCode: code ?? SIGNAL_EXIT_BASE + (killedBy === null ? 0 : constants.signals[killedBy])
6753
+ });
6754
+ });
6755
+ });
6756
+ function failedError(command, { exitCode, stderr }) {
6757
+ const tail = lastLines(stderr, STDERR_TAIL_LINES);
6758
+ return new CliError({
6759
+ code: TOOL_FAILED,
6760
+ message: tail === "" ? `${command} exited with ${exitCode} and wrote nothing to stderr` : `${command} exited with ${exitCode}; its stderr ends:\n${tail}`,
6761
+ hint: `the output of ${command} above says why it failed`
6762
+ });
6763
+ }
6764
+ function missingError(command, cause) {
6765
+ return new CliError({
6766
+ code: TOOL_MISSING,
6767
+ message: `${command} could not be started: it is not on PATH`,
6768
+ hint: `install ${command}, then run the same command again`
6769
+ }, { cause });
6770
+ }
6771
+ /** `ctx.spawn`: `spawn` bound to the run, its start failures classified by errno. */
6772
+ function contextSpawn(run, defaults, callerLine) {
6773
+ return async (command, args = [], options = {}) => {
6774
+ let result;
6775
+ try {
6776
+ result = await run(command, args, {
6777
+ cwd: options.cwd ?? defaults.cwd,
6778
+ env: options.env ?? defaults.env,
6779
+ signal: defaults.signal
6780
+ });
6781
+ } catch (error) {
6782
+ if (isMissingFile(error)) throw missingError(command, error);
6783
+ throw classifyErrno(error, {
6784
+ path: command,
6785
+ access: "read",
6786
+ callerLine,
6787
+ env: defaults.env
6788
+ });
6789
+ }
6790
+ if (options.check === true && result.exitCode !== 0) throw failedError(command, result);
6791
+ return result;
6792
+ };
6793
+ }
6794
+ //#endregion
6795
+ //#region ../agent-cli/dist/exit-classes.js
6796
+ const SIGNAL_EXIT_CODES = {
6797
+ SIGINT: 130,
6798
+ SIGTERM: 143
6799
+ };
6800
+ const EXIT_CODES = {
6801
+ caller: 1,
6802
+ operator: 2,
6803
+ source: 3,
6804
+ wait: 75,
6805
+ bug: 70
6806
+ };
6807
+ /** The codes the Runtime and its built-in Commands raise, each with the class that must act on it. */
6808
+ function runtimeCodes() {
6809
+ return {
6810
+ [INVALID_INPUT]: "caller",
6811
+ [UNKNOWN_OPTION]: "caller",
6812
+ [UNKNOWN_COMMAND]: "caller",
6813
+ [UNKNOWN_FIELD]: "caller",
6814
+ [CONFIRMATION_REQUIRED]: "caller",
6815
+ [CONFIRMATION_MISMATCH]: "caller",
6816
+ [NOT_CONFIRMED]: "operator",
6817
+ [OUTPUT_EXISTS]: "caller",
6818
+ [INTERRUPTED]: "interrupted",
6819
+ [DEADLINE_EXCEEDED]: "wait",
6820
+ [INTERNAL_CODE]: "bug",
6821
+ [PERMISSION_DENIED]: "operator",
6822
+ [DISK_FULL]: "operator",
6823
+ [NOT_FOUND]: "caller",
6824
+ [ALREADY_EXISTS]: "caller",
6825
+ [TOOL_FAILED]: "operator",
6826
+ [TOOL_MISSING]: "operator",
6827
+ [PROJECT_INVALID]: "operator",
6828
+ [DOCTOR_PROBLEMS]: "operator",
6829
+ [NOT_GLOBAL_INSTALL]: "operator",
6830
+ [REGISTRY_UNREACHABLE]: "operator",
6831
+ [REGISTRY_AUTH]: "operator",
6832
+ [INSTALL_FAILED]: "operator",
6833
+ [NPM_FAILED]: "operator",
6834
+ [NO_LATEST]: "source"
6835
+ };
6836
+ }
6837
+ /** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
6838
+ function runtimeCodesIn(errors) {
6839
+ return Object.keys(errors).filter((code) => Object.hasOwn(runtimeCodes(), code));
6840
+ }
6841
+ function exitClassOfOne(error, errors) {
6842
+ if (!(error instanceof CliError)) return "bug";
6843
+ if (error instanceof UsageError) return "caller";
6844
+ return runtimeCodes()[error.code] ?? errors[error.code]?.class ?? "bug";
6845
+ }
6846
+ function isBug(error, errors) {
6847
+ return exitClassOfOne(error, errors) === "bug";
6848
+ }
6849
+ const ACT_FIRST = [
6850
+ "caller",
6851
+ "operator",
6852
+ "source",
6853
+ "wait",
6854
+ "interrupted"
6855
+ ];
6856
+ function exitClassOf(error, errors) {
6857
+ const classes = errorsOf(error).map((each) => exitClassOfOne(each, errors));
6858
+ if (classes.includes("bug")) return "bug";
6859
+ return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
6860
+ }
6861
+ /**
6862
+ * The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime's
6863
+ * codes it can reach, and those of its own table.
6864
+ */
6865
+ function reachableExitClasses(cli) {
6866
+ const runtime = [
6867
+ "caller",
6868
+ "operator",
6869
+ "wait",
6870
+ ...cli.commands !== void 0 && cli.install !== void 0 ? ["source"] : []
6871
+ ];
6872
+ const own = Object.values(cli.errors ?? {}).map((declaration) => declaration.class);
6873
+ return /* @__PURE__ */ new Set([...runtime, ...own]);
6874
+ }
6875
+ /** The signal named by an interrupt's abort reason; any other reason counts as Ctrl-C. */
6876
+ function stopSignalOf(reason) {
6877
+ return reason === "SIGTERM" ? "SIGTERM" : "SIGINT";
6878
+ }
6879
+ /**
6880
+ * The exit code of a run that failed with `error`, given the CLI's error table; `stop` is the
6881
+ * signal that interrupted it, if any.
6882
+ */
6883
+ function exitCodeOf(error, errors, stop) {
6884
+ const exitClass = exitClassOf(error, errors);
6885
+ return exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass];
6886
+ }
6887
+ //#endregion
6888
+ //#region ../agent-cli/dist/help.js
6889
+ function table(rows) {
6890
+ const width = Math.max(...rows.map(([left]) => left.length));
6891
+ return rows.map(([left, right]) => right === "" ? ` ${left}` : ` ${left.padEnd(width)} ${right}`);
6892
+ }
6893
+ function section(title, rows, style) {
6894
+ return rows.length === 0 ? [] : [
6895
+ "",
6896
+ style.bold(`${title}:`),
6897
+ ...table(rows)
6898
+ ];
6899
+ }
6900
+ function summaryOf(node) {
6901
+ return node.kind === "command" ? node.description : `group: ${Object.keys(node.commands).join(", ")}`;
6902
+ }
6903
+ function commandRows({ commands }) {
6904
+ return Object.entries(commands).map(([key, node]) => [key, summaryOf(node)]);
6905
+ }
6906
+ function lines(...parts) {
6907
+ return `${parts.join("\n")}\n`;
6908
+ }
6909
+ function listingLines(heading, group, style) {
6910
+ return [
6911
+ style.bold(heading),
6912
+ ...section("Commands", commandRows(group), style),
6913
+ "",
6914
+ `Run \`${group.commandLine} <command> --help\` for its examples and input.`
6915
+ ];
6916
+ }
6917
+ const EXIT_MEANINGS = {
6918
+ caller: "the caller fixes its command",
6919
+ operator: "the operator acts on their machine",
6920
+ source: "someone at the source acts",
6921
+ wait: "nobody acts: wait, then run it again"
6922
+ };
6923
+ function exitCodeRows(cli) {
6924
+ return [
6925
+ ["0", "done"],
6926
+ ...[...[...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]),
6927
+ [Object.values(SIGNAL_EXIT_CODES).join(", "), "interrupted"]
6928
+ ];
6929
+ }
6930
+ function firstExample(node) {
6931
+ if (node.kind === "command") return node.examples.slice(0, 1);
6932
+ return Object.values(node.commands).flatMap(firstExample).slice(0, 1);
6933
+ }
6934
+ function rootExamples(root) {
6935
+ if (root.kind === "command") return [...root.examples];
6936
+ const { doctor, ...others } = root.commands;
6937
+ return [...Object.values(others), ...doctor === void 0 ? [] : [doctor]].flatMap(firstExample);
6938
+ }
6939
+ function examplesSection(examples, style) {
6940
+ return section("Examples", examples.map(({ command, note }) => [command, note]), style);
6941
+ }
6942
+ /** The CLI's own help: a listing or its single Command's help, ending with exit codes and examples. */
6943
+ function cliHelp(cli, root, style) {
6944
+ 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));
6945
+ }
6946
+ function groupHelp(group, style) {
6947
+ return lines(...listingLines(group.commandLine, group, style));
6948
+ }
6949
+ const EFFECT_NOTES = {
6950
+ read: "changes nothing",
6951
+ write: "changes something; show the Plan with --dry-run",
6952
+ destructive: "changes something hard to undo; show the Plan with --dry-run, run with --confirm",
6953
+ serve: "changes nothing; prints its data at once, then runs until Ctrl-C, which exits 0"
6954
+ };
6955
+ function choicesOf(field) {
6956
+ return field.choices === void 0 ? [] : [`one of: ${field.choices.join(", ")}`];
6957
+ }
6958
+ function defaultsOf(field) {
6959
+ if (field.defaultValue === void 0) return [];
6960
+ if (field.type !== "boolean") return [`default: ${JSON.stringify(field.defaultValue)}`];
6961
+ return field.defaultValue === true ? ["on by default; this flag turns it off"] : [];
6962
+ }
6963
+ function fieldRow(field, notes) {
6964
+ const all = [...field.required ? ["required"] : [], ...notes];
6965
+ const suffix = all.length === 0 ? "" : `(${all.join("; ")})`;
6966
+ return [field.usage, [field.description ?? "", suffix].filter((part) => part !== "").join(" ")];
6967
+ }
6968
+ function argRow(field) {
6969
+ return fieldRow(field, [...choicesOf(field), ...defaultsOf(field)]);
6970
+ }
6971
+ function optionRow(field) {
6972
+ return fieldRow(field, defaultsOf(field));
6973
+ }
6974
+ function flagRow({ usage, description }) {
6975
+ return [usage, description];
6976
+ }
6977
+ function commandLines(spec, style, { examples }) {
6978
+ return [
6979
+ style.bold(spec.commandLine),
6980
+ spec.description,
6981
+ ...examples ? examplesSection(spec.examples, style) : [],
6982
+ ...section("Arguments", spec.args.map(argRow), style),
6983
+ ...section("Options", spec.options.map(optionRow), style),
6984
+ "",
6985
+ `${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`,
6986
+ ...section("Standard flags", spec.standardFlags.map(flagRow), style)
6987
+ ];
6988
+ }
6989
+ function commandHelp(spec, style) {
6990
+ return lines(...commandLines(spec, style, { examples: true }));
6991
+ }
6992
+ //#endregion
6993
+ //#region ../agent-cli/dist/help-json.js
6994
+ function inputSchema(schema) {
6995
+ return toJSONSchema(schema, {
6996
+ io: "input",
6997
+ unrepresentable: "any"
6998
+ });
6999
+ }
7000
+ function optionsSchema(options) {
7001
+ return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
7002
+ }
7003
+ function commandData(spec) {
7004
+ return {
7005
+ name: spec.commandLine,
7006
+ description: spec.description,
7007
+ effect: spec.effect,
7008
+ examples: spec.examples,
7009
+ args: inputSchema(spec.command.args ?? object({})),
7010
+ options: optionsSchema(spec.options),
7011
+ standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
7012
+ usage,
7013
+ description,
7014
+ schema: inputSchema(schema)
7015
+ })),
7016
+ output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
7017
+ };
7018
+ }
7019
+ function nodeData(node) {
7020
+ return node.kind === "command" ? commandData(node) : groupData(node);
7021
+ }
7022
+ function groupData(group) {
7023
+ return {
7024
+ name: group.commandLine,
7025
+ commands: Object.values(group.commands).map(nodeData)
7026
+ };
7027
+ }
7028
+ function printed$1(data) {
7029
+ return `${JSON.stringify(data)}\n`;
7030
+ }
7031
+ function errorsData(errors) {
7032
+ return Object.fromEntries(Object.entries(errors).map(([code, declaration]) => [code, {
7033
+ class: declaration.class,
7034
+ details: declaration.details === void 0 ? null : toJSONSchema(declaration.details, { unrepresentable: "any" })
7035
+ }]));
7036
+ }
7037
+ function cliHelpJson({ version, errors }, root) {
7038
+ return printed$1({
7039
+ name: root.commandLine,
7040
+ version,
7041
+ ...nodeData(root),
7042
+ errors: errorsData(errors ?? {})
7043
+ });
7044
+ }
7045
+ function groupHelpJson(group) {
7046
+ return printed$1(groupData(group));
7047
+ }
7048
+ function commandHelpJson(spec) {
7049
+ return printed$1(commandData(spec));
7050
+ }
7051
+ //#endregion
7052
+ //#region ../agent-cli/dist/output.js
7053
+ const SUCCESS_EXIT_CODE = 0;
7054
+ function withTrailingNewline(text) {
7055
+ return text === "" || text.endsWith("\n") ? text : `${text}\n`;
7056
+ }
7057
+ function printed(value, render, { json, invocation, style, fields }) {
7058
+ if (!(value instanceof Page)) return json ? `${JSON.stringify(value)}\n` : withTrailingNewline(render(style));
7059
+ const page = printedPage(value, invocation);
7060
+ if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
7061
+ const rows = projected(page.items, fields);
7062
+ return json ? `${JSON.stringify({
7063
+ ...page,
7064
+ items: rows
7065
+ })}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
7066
+ }
7067
+ function errorText(error, format) {
7068
+ const text = formatRuntimeError(error, {
7069
+ ...format,
7070
+ isBug: (each) => isBug(each, format.errors)
7071
+ });
7072
+ const { usageHelp, json } = format;
7073
+ return usageHelp !== void 0 && !json && error instanceof UsageError ? `${text}\n${usageHelp}` : text;
7074
+ }
7075
+ function stdoutOf({ value, render }, format, spill) {
7076
+ const text = printed(value, render, format);
7077
+ return value instanceof Page ? text : spill(text, format.json);
7078
+ }
7079
+ /** What a serve Command prints the moment it starts. */
7080
+ function startedOutput(outcome, format, spill) {
7081
+ return stdoutOf(outcome, {
7082
+ ...format,
7083
+ fields: void 0
7084
+ }, spill);
7085
+ }
7086
+ /** A serve Command stopped after it started: stopping it is how it is meant to end. */
7087
+ const STOPPED_RESULT = {
7088
+ stdout: "",
7089
+ stderr: "",
7090
+ exitCode: SUCCESS_EXIT_CODE
7091
+ };
7092
+ function errorResult(error, format) {
7093
+ return {
7094
+ stdout: "",
7095
+ stderr: errorText(error, format),
7096
+ exitCode: exitCodeOf(error, format.errors, format.stop)
7097
+ };
7098
+ }
7099
+ async function commandResult(settled, format, spill) {
7100
+ if (settled.ok) try {
7101
+ return {
7102
+ stdout: await stdoutOf(settled.outcome, format, spill),
7103
+ stderr: "",
7104
+ exitCode: SUCCESS_EXIT_CODE
7105
+ };
7106
+ } catch (spillError) {
7107
+ return errorResult(spillError, format);
7108
+ }
7109
+ const { error, delivered } = settled;
7110
+ const result = errorResult(error, format);
7111
+ if (delivered === void 0) return result;
7112
+ try {
7113
+ return {
7114
+ ...result,
7115
+ stdout: await stdoutOf(delivered, format, spill)
7116
+ };
7117
+ } catch (spillError) {
7118
+ const both = new AggregateError([...errorsOf(error), spillError]);
7119
+ return {
7120
+ ...result,
7121
+ stderr: errorText(both, format)
7122
+ };
7123
+ }
7124
+ }
7125
+ function textHelp(node, root, cli, style) {
7126
+ if (node === root) return cliHelp(cli, node, style);
7127
+ return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
7128
+ }
7129
+ function helpOf(node, root, cli, style, json) {
7130
+ if (!json) return textHelp(node, root, cli, style);
7131
+ if (node === root) return cliHelpJson(cli, node);
7132
+ return node.kind === "command" ? commandHelpJson(node) : groupHelpJson(node);
7133
+ }
7134
+ function helpResult(node, root, cli, { style, json }) {
7135
+ return {
7136
+ stdout: helpOf(node, root, cli, style, json),
7137
+ stderr: "",
7138
+ exitCode: SUCCESS_EXIT_CODE
7139
+ };
7140
+ }
7141
+ /** The first line of `--version`: the version, and the commit and time of a stamped build. */
7142
+ function versionLine({ version, build }) {
7143
+ return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
7144
+ }
7145
+ function versionResult(cli, packageRoot) {
7146
+ return {
7147
+ stdout: `${(cli.build === void 0 ? [versionLine(cli)] : [versionLine(cli), `changelog ${join(packageRoot, "CHANGELOG.md")}`]).join("\n")}\n`,
7148
+ stderr: "",
7149
+ exitCode: SUCCESS_EXIT_CODE
7150
+ };
7151
+ }
7152
+ //#endregion
7153
+ //#region ../agent-cli/dist/skills.js
7154
+ /** Where the skills CLI (`npx skills add`) puts a skill, under a project and under home. */
7155
+ const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7156
+ function skillUpdateCommand(cliName) {
7157
+ return `npx -y skills update ${cliName}`;
7158
+ }
7159
+ const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
7160
+ const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
7161
+ /** The version a SKILL.md's frontmatter `metadata.version` states. */
7162
+ function skillVersionIn(skill) {
7163
+ const frontmatter = FRONTMATTER.exec(skill)?.[1];
7164
+ return frontmatter === void 0 ? void 0 : METADATA_VERSION.exec(`${frontmatter}\n`)?.[1];
7165
+ }
7166
+ function homeOf(env) {
7167
+ return env.HOME ?? env.USERPROFILE ?? homedir();
7168
+ }
7169
+ async function copyAt(path, cliVersion) {
7170
+ let skill;
7171
+ try {
7172
+ skill = await readFile(join(path, "SKILL.md"), "utf8");
7173
+ } catch (error) {
7174
+ if (isMissingFile(error)) return;
7175
+ return {
7176
+ path,
7177
+ version: void 0,
7178
+ state: "unreadable"
7179
+ };
7180
+ }
7181
+ const version = skillVersionIn(skill);
7182
+ if (version === void 0 || !isVersion(version) || !isVersion(cliVersion)) return {
7183
+ path,
7184
+ version,
7185
+ state: "unversioned"
7186
+ };
7187
+ return {
7188
+ path,
7189
+ version,
7190
+ state: standingOf(version, cliVersion)
7191
+ };
7192
+ }
7193
+ /** Every installed copy of the skill named after the CLI, in the project first, then under home. */
7194
+ async function skillCopies(skillName, cliVersion, cwd, env) {
7195
+ const paths = [cwd, homeOf(env)].flatMap((root) => SKILL_DIRS.map((dir) => join(root, ...dir, skillName)));
7196
+ const seen = /* @__PURE__ */ new Set();
7197
+ const copies = [];
7198
+ for (const path of paths) {
7199
+ const copy = await copyAt(path, cliVersion);
7200
+ const real = copy === void 0 ? void 0 : await realpath(path);
7201
+ if (copy !== void 0 && real !== void 0 && !seen.has(real)) {
7202
+ seen.add(real);
7203
+ copies.push(copy);
7204
+ }
7205
+ }
7206
+ return copies;
7207
+ }
7208
+ //#endregion
7209
+ //#region ../agent-cli/dist/doctor-rows.js
7210
+ function ok(message) {
7211
+ return {
7212
+ state: "ok",
7213
+ message
7214
+ };
7215
+ }
7216
+ function buildRow(cli) {
7217
+ return {
7218
+ name: "build",
7219
+ run: () => ok(versionLine(cli))
7220
+ };
7221
+ }
7222
+ function nodeRow(cli) {
7223
+ return {
7224
+ name: "node",
7225
+ run: () => {
7226
+ const found = process.versions.node;
7227
+ const range = cli.engines?.node;
7228
+ if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
7229
+ const minimum = engineMinimum(range);
7230
+ if (minimum === void 0) return {
7231
+ state: "problem",
7232
+ message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
7233
+ };
7234
+ if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
7235
+ const major = minimum[0];
7236
+ return {
7237
+ state: "problem",
7238
+ message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
7239
+ fix: `nvm install ${major} && nvm alias default ${major}`
7240
+ };
7241
+ }
7242
+ };
7243
+ }
7244
+ function projectRow(projectFile) {
7245
+ const { file } = projectFile;
7246
+ return {
7247
+ name: "project",
7248
+ run: (ctx) => {
7249
+ let project;
7250
+ try {
7251
+ project = projectOf(projectFile, ctx.cwd);
7252
+ } catch (error) {
7253
+ if (error instanceof CliError && error.code === "PROJECT_INVALID") return {
7254
+ state: "problem",
7255
+ message: error.message
7256
+ };
7257
+ throw error;
7258
+ }
7259
+ return ok(project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(project.root, file));
7260
+ }
7261
+ };
7262
+ }
7263
+ function installRow(cli, bin) {
7264
+ return {
7265
+ name: "install",
7266
+ advisory: true,
7267
+ run: async (ctx) => {
7268
+ const { install } = cli;
7269
+ if (install === void 0) return ok(`${cli.name} declares no install, so it has no \`update\``);
7270
+ try {
7271
+ return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx))).prefix}`);
7272
+ } catch (error) {
7273
+ if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
7274
+ state: "problem",
7275
+ message: error.message,
7276
+ fix: installCommandLine(install)
7277
+ };
7278
+ throw error;
7279
+ }
7280
+ }
7281
+ };
7282
+ }
7283
+ function registryRow(cli) {
7284
+ return {
7285
+ name: "registry",
7286
+ advisory: true,
7287
+ run: async (ctx) => {
7288
+ const { install } = cli;
7289
+ if (install === void 0) return ok(`${cli.name} declares no registry to compare this build with`);
7290
+ const latest = await latestVersion(npmContext(cli, install, ctx));
7291
+ const standing = standingOf(cli.version, latest);
7292
+ const head = `${install.registry} has ${latest} as latest: this build`;
7293
+ if (standing === "behind") return {
7294
+ state: "problem",
7295
+ message: `${head} (${cli.version}) is older`,
6285
7296
  fix: `${cli.name} update`
6286
7297
  };
6287
7298
  return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
@@ -6318,10 +7329,14 @@ function skillRow(cli) {
6318
7329
  }
6319
7330
  };
6320
7331
  }
6321
- /** The rows every `doctor` reports first, in this order. */
6322
- function fixedRows(cli, bin) {
7332
+ /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
7333
+ function doctorChecks(cli, bin) {
6323
7334
  return [
7335
+ buildRow(cli),
6324
7336
  nodeRow(cli),
7337
+ ...cli.project === void 0 ? [] : [projectRow(cli.project)],
7338
+ ...cli.doctor ?? [],
7339
+ ...toolRows(cli.commands),
6325
7340
  installRow(cli, bin),
6326
7341
  registryRow(cli),
6327
7342
  skillRow(cli)
@@ -6329,7 +7344,8 @@ function fixedRows(cli, bin) {
6329
7344
  }
6330
7345
  //#endregion
6331
7346
  //#region ../agent-cli/dist/doctor.js
6332
- const PROBE_DEADLINE_MS = 1e4;
7347
+ const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
7348
+ const DEFAULT_DEADLINE_MS = 1e4;
6333
7349
  const MS_PER_SECOND = 1e3;
6334
7350
  const MIN_LABEL_WIDTH = 8;
6335
7351
  const reportSchema = object({
@@ -6341,19 +7357,10 @@ const reportSchema = object({
6341
7357
  })),
6342
7358
  next: array(string())
6343
7359
  });
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 }) {
7360
+ function problemOf(check, error, { signal, deadline, ms }) {
6354
7361
  if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
6355
7362
  state: "problem",
6356
- message: `${check.name} did not answer within ${PROBE_DEADLINE_MS / MS_PER_SECOND} s`
7363
+ message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
6357
7364
  };
6358
7365
  if (error instanceof CliError) return {
6359
7366
  state: "problem",
@@ -6364,23 +7371,28 @@ function problemOf(check, error, { signal, deadline }) {
6364
7371
  message: `the ${check.name} row hit a bug: ${messageOf(error)}`
6365
7372
  };
6366
7373
  }
7374
+ function withSignal(ctx, signal) {
7375
+ return Object.create(ctx, { signal: { value: signal } });
7376
+ }
6367
7377
  async function rowOf(check, ctx) {
6368
- const signal = ctx.deadline(PROBE_DEADLINE_MS);
7378
+ const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
7379
+ const signal = ctx.deadline(ms);
6369
7380
  let finding;
6370
7381
  try {
6371
- finding = await Promise.race([Promise.resolve().then(() => check.run({
6372
- ...ctx,
6373
- signal
6374
- })), untilAborted(signal)]);
7382
+ finding = await Promise.race([Promise.resolve().then(() => check.run(withSignal(ctx, signal))), untilAborted(signal)]);
6375
7383
  } catch (error) {
6376
7384
  finding = problemOf(check, error, {
6377
7385
  signal: ctx.signal,
6378
- deadline: signal
7386
+ deadline: signal,
7387
+ ms
6379
7388
  });
6380
7389
  }
6381
7390
  return {
6382
- check: check.name,
6383
- ...finding
7391
+ row: {
7392
+ check: check.name,
7393
+ ...finding
7394
+ },
7395
+ advisory: check.advisory === true
6384
7396
  };
6385
7397
  }
6386
7398
  function nextOf(rows) {
@@ -6394,18 +7406,16 @@ function render({ rows, next }, style) {
6394
7406
  return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
6395
7407
  }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
6396
7408
  }
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",
7409
+ function failure(cliName, problems, report) {
7410
+ return new CliError({
7411
+ code: DOCTOR_PROBLEMS,
6401
7412
  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);
7413
+ hint: report.next.length === 0 ? "each problem row says what is wrong" : "run the commands under next, then run doctor again"
7414
+ });
6405
7415
  }
6406
- /** The built-in `doctor`: the fixed rows, then the CLI's own, each under its own deadline. */
7416
+ /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
6407
7417
  function doctorCommand(cli, bin) {
6408
- return defineCommand({
7418
+ return defineCommand$1({
6409
7419
  name: "doctor",
6410
7420
  description: "check the setup and say what fixes each problem",
6411
7421
  examples: [{
@@ -6418,120 +7428,24 @@ function doctorCommand(cli, bin) {
6418
7428
  effect: "read",
6419
7429
  output: reportSchema,
6420
7430
  run: async (_input, ctx) => {
6421
- const checks = [...fixedRows(cli, bin), ...cli.doctor ?? []];
6422
- const rows = await Promise.all(checks.map((check) => rowOf(check, ctx)));
7431
+ const checked = await Promise.all(doctorChecks(cli, bin).map((check) => rowOf(check, ctx)));
7432
+ ctx.signal.throwIfAborted();
7433
+ const rows = checked.map(({ row }) => row);
6423
7434
  const report = {
6424
7435
  rows,
6425
7436
  next: nextOf(rows)
6426
7437
  };
6427
- if (rows.some(({ state }) => state === "problem")) throw failure(cli.name, report);
7438
+ const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row.check);
7439
+ if (problems.length > 0) {
7440
+ ctx.deliver(report);
7441
+ throw failure(cli.name, problems, report);
7442
+ }
6428
7443
  return report;
6429
7444
  },
6430
7445
  render
6431
7446
  });
6432
7447
  }
6433
7448
  //#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
7449
  //#region ../agent-cli/dist/upgrade-notes.js
6536
7450
  const RELEASE_HEADING = /^## v?(\S+)/;
6537
7451
  function releasesIn(changelog) {
@@ -6595,17 +7509,16 @@ function renderUpdate(cli, install, update) {
6595
7509
  ...update.next.map((command) => `next: ${command}, so the ${cli.name} skill describes ${installed.version}`)
6596
7510
  ].join("\n");
6597
7511
  }
6598
- function interruptedAfterInstall(plan, update) {
6599
- return new FailureWithData({
7512
+ function interruptedAfterInstall(plan) {
7513
+ return new CliError({
6600
7514
  code: INTERRUPTED,
6601
7515
  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);
7516
+ hint: "nothing to run again: run the commands under next"
7517
+ });
6605
7518
  }
6606
7519
  /** The built-in `update`, for a CLI installed globally with npm. */
6607
7520
  function updateCommand(cli, install, bin) {
6608
- return defineCommand({
7521
+ return defineCommand$1({
6609
7522
  name: "update",
6610
7523
  description: "install the latest version of this CLI from its registry",
6611
7524
  examples: [{
@@ -6639,7 +7552,7 @@ function updateCommand(cli, install, bin) {
6639
7552
  };
6640
7553
  ctx.signal.throwIfAborted();
6641
7554
  ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
6642
- await ctx.runToEnd(() => installGlobally(latest, npmContext(cli, install, ctx)));
7555
+ await ctx.runToEnd(`installing ${plan.package}@${latest}`, () => installGlobally(latest, npmContext(cli, install, ctx)));
6643
7556
  const update = {
6644
7557
  from,
6645
7558
  latest,
@@ -6650,7 +7563,10 @@ function updateCommand(cli, install, bin) {
6650
7563
  upgrades: await upgradeNotes(plan.packageDir, from, latest),
6651
7564
  next: [skillUpdateCommand(cli.name)]
6652
7565
  };
6653
- if (ctx.signal.aborted) throw interruptedAfterInstall(plan, update);
7566
+ if (ctx.signal.aborted) {
7567
+ ctx.deliver(update);
7568
+ throw interruptedAfterInstall(plan);
7569
+ }
6654
7570
  return update;
6655
7571
  },
6656
7572
  render: (update) => renderUpdate(cli, install, update),
@@ -6666,263 +7582,255 @@ function builtInCommands(cli, bin) {
6666
7582
  ...cli.install === void 0 ? {} : { update: updateCommand(cli, cli.install, bin) }
6667
7583
  };
6668
7584
  }
6669
- function defineCli(cli) {
7585
+ //#endregion
7586
+ //#region ../agent-cli/dist/agent-cli.js
7587
+ function checkOwnCodes(cli) {
7588
+ const redeclared = runtimeCodesIn(cli.errors ?? {});
7589
+ if (redeclared.length > 0) throw new Error(`${cli.name} declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
7590
+ }
7591
+ function defineCli$1(cli) {
7592
+ checkOwnCodes(cli);
6670
7593
  if (cli.commands === void 0) return cli;
6671
7594
  const { commands } = cli;
6672
7595
  const taken = Object.keys(builtInCommands(cli, void 0)).filter((name) => commands[name] !== void 0);
6673
7596
  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]))));
6818
- }
6819
- function commandData(spec) {
6820
- 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" })
7597
+ return cli;
7598
+ }
7599
+ /**
7600
+ * Declares the CLI's ports and its Project file's settings once; the `defineCommand` and
7601
+ * `defineCli` it returns type `ctx.ports` and `ctx.project`.
7602
+ */
7603
+ function agentCli() {
7604
+ return {
7605
+ defineCommand: defineCommandFor(),
7606
+ defineCli: defineCli$1
6833
7607
  };
6834
7608
  }
6835
- function nodeData(node) {
6836
- return node.kind === "command" ? commandData(node) : groupData(node);
7609
+ //#endregion
7610
+ //#region ../agent-cli/dist/error-table.js
7611
+ /** Keeps each code's literal class and `details` schema, so `cliError` can type its throw sites. */
7612
+ function defineErrors(table) {
7613
+ return table;
6837
7614
  }
6838
- function groupData(group) {
7615
+ /** A `CliError` for a code of `table`, whose `details` must match the code's schema. */
7616
+ function cliError(_table, code, init, options) {
7617
+ return new CliError({
7618
+ ...init,
7619
+ code
7620
+ }, options);
7621
+ }
7622
+ //#endregion
7623
+ //#region ../agent-cli/dist/command-spec.js
7624
+ function declaredOf(command) {
7625
+ if (!hasPlan(command)) return {
7626
+ outputs: false,
7627
+ force: void 0,
7628
+ cost: false
7629
+ };
6839
7630
  return {
6840
- name: group.commandLine,
6841
- commands: Object.values(group.commands).map(nodeData)
7631
+ outputs: command.outputs !== void 0,
7632
+ force: command.force,
7633
+ cost: command.cost !== void 0
6842
7634
  };
6843
7635
  }
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
- });
7636
+ function commandSpec(commandLine, command) {
7637
+ const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
7638
+ return {
7639
+ kind: "command",
7640
+ commandLine,
7641
+ command,
7642
+ description: command.description,
7643
+ effect: command.effect,
7644
+ examples: command.examples,
7645
+ args: argFields(command.args),
7646
+ options: optionFields(command.options),
7647
+ standardFlags: standardFlagsFor(command.effect, declaredOf(command), list),
7648
+ list,
7649
+ output: list?.output ?? command.output
7650
+ };
6853
7651
  }
6854
- function groupHelpJson(group) {
6855
- return printed$1(groupData(group));
7652
+ function groupSpec(commandLine, commands) {
7653
+ return {
7654
+ kind: "group",
7655
+ commandLine,
7656
+ commands: Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node)]))
7657
+ };
6856
7658
  }
6857
- function commandHelpJson(spec) {
6858
- return printed$1(commandData(spec));
7659
+ function specOf(commandLine, node) {
7660
+ return isCommand(node) ? commandSpec(commandLine, node) : groupSpec(commandLine, node);
6859
7661
  }
6860
7662
  //#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`;
7663
+ //#region ../agent-cli/dist/execute.js
7664
+ function outcomeOf(value, render) {
7665
+ return {
7666
+ value,
7667
+ render: (style) => render(value, style)
7668
+ };
6867
7669
  }
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)}`;
7670
+ /** Delivered data as the Command prints it, which is the data its `run` or `apply` returns. */
7671
+ function deliveredOutcome(command, data) {
7672
+ return outcomeOf(data, command.render);
6877
7673
  }
6878
- function errorResult(error, format) {
7674
+ function dryRunOutcome(command, plan) {
7675
+ const renderPlan = (style) => command.renderPlan(plan, style);
7676
+ if (command.outputs === void 0 && command.cost === void 0) return {
7677
+ value: plan,
7678
+ render: renderPlan
7679
+ };
7680
+ const outputs = command.outputs?.(plan);
7681
+ const cost = command.cost?.(plan);
6879
7682
  return {
6880
- stdout: "",
6881
- stderr: formatRuntimeError(error, format),
6882
- exitCode: error instanceof UsageError ? USAGE_EXIT_CODE : FAILURE_EXIT_CODE
7683
+ value: {
7684
+ plan,
7685
+ ...outputs === void 0 ? {} : { outputs },
7686
+ ...command.cost === void 0 ? {} : { cost: cost ?? null }
7687
+ },
7688
+ render: (style) => [
7689
+ renderPlan(style),
7690
+ ...outputs === void 0 ? [] : ["outputs:", ...outputs.map((path) => ` ${path}`)],
7691
+ ...cost === void 0 ? [] : [`cost: ${cost}`]
7692
+ ].join("\n")
6883
7693
  };
6884
7694
  }
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
- };
7695
+ async function execute(command, input, ctx, { dryRun, confirm, yes, page, invocation, commandLine, started }) {
7696
+ await checkTools(command.tools ?? [], ctx.env);
7697
+ if (command.rows !== void 0) return outcomeOf(await command.run({
7698
+ ...input,
7699
+ ...page
7700
+ }, ctx), (data, style) => command.render(data, style));
7701
+ if (command.effect === "read") return outcomeOf(await command.run(input, ctx), (data, style) => command.render(data, style));
7702
+ if (command.effect === "serve") {
7703
+ await started(outcomeOf(await command.start(input, ctx), (value, style) => command.render(value, style)));
7704
+ return untilAborted(ctx.signal);
6893
7705
  }
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
- };
7706
+ const plan = await command.plan(input, ctx);
7707
+ if (dryRun) return dryRunOutcome(command, plan);
7708
+ await checkOutputs({
7709
+ invocation,
7710
+ commandLine,
7711
+ force: ctx.force,
7712
+ files: ctx.files
7713
+ }, command.outputs?.(plan));
7714
+ checkCost({
7715
+ invocation,
7716
+ commandLine,
7717
+ yes
7718
+ }, command.cost?.(plan));
7719
+ if (command.effect === "destructive") checkConfirmation({
7720
+ invocation,
7721
+ commandLine,
7722
+ given: confirm
7723
+ }, command.confirm(plan));
7724
+ return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
6902
7725
  }
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);
7726
+ //#endregion
7727
+ //#region ../agent-cli/dist/files.js
7728
+ const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
7729
+ function tempPathOf(path) {
7730
+ return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
7731
+ }
7732
+ function isAlive(pid) {
7733
+ try {
7734
+ process.kill(pid, 0);
7735
+ return true;
7736
+ } catch (error) {
7737
+ return errnoOf(error) === "EPERM";
6907
7738
  }
6908
- if (node.kind === "command") return commandHelp(node, style);
6909
- return node === root ? cliHelp(cli.version, node, style) : groupHelp(node, style);
6910
7739
  }
6911
- function helpResult(node, root, cli, { style, json }) {
7740
+ async function sweep(folder) {
7741
+ const dead = (await readdir(folder)).filter((name) => {
7742
+ const pid = TEMP_FILE.exec(name)?.[1];
7743
+ return pid !== void 0 && !isAlive(Number(pid));
7744
+ });
7745
+ await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
7746
+ }
7747
+ /** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
7748
+ function realFiles() {
7749
+ const swept = /* @__PURE__ */ new Set();
6912
7750
  return {
6913
- stdout: helpOf(node, root, cli, style, json),
6914
- stderr: "",
6915
- exitCode: SUCCESS_EXIT_CODE
7751
+ exists: async (path) => {
7752
+ try {
7753
+ await access(path);
7754
+ return true;
7755
+ } catch (error) {
7756
+ if (isMissingFile(error)) return false;
7757
+ throw error;
7758
+ }
7759
+ },
7760
+ write: async (path, data) => {
7761
+ const folder = dirname(path);
7762
+ await mkdir(folder, { recursive: true });
7763
+ if (!swept.has(folder)) {
7764
+ await sweep(folder);
7765
+ swept.add(folder);
7766
+ }
7767
+ const temp = tempPathOf(path);
7768
+ try {
7769
+ await writeFile(temp, data);
7770
+ await rename(temp, path);
7771
+ } catch (error) {
7772
+ await rm(temp, { force: true });
7773
+ throw error;
7774
+ }
7775
+ },
7776
+ read: (path) => readFile(path),
7777
+ list: (folder) => readdir(folder),
7778
+ remove: (path) => rm(path, {
7779
+ recursive: true,
7780
+ force: true
7781
+ }),
7782
+ mkdir: async (path, options) => {
7783
+ if (options?.exclusive !== true) {
7784
+ await mkdir(path, { recursive: true });
7785
+ return;
7786
+ }
7787
+ await mkdir(dirname(path), { recursive: true });
7788
+ await mkdir(path);
7789
+ }
6916
7790
  };
6917
7791
  }
6918
- function versionResult({ version, build }) {
7792
+ /**
7793
+ * `files` resolving paths against `cwd`, its failures classified by errno; `list` drops the
7794
+ * Runtime's temp files and sorts.
7795
+ */
7796
+ function classifiedFiles(files, { cwd, env, callerLine }) {
7797
+ async function at(access, path, work) {
7798
+ const target = resolve(cwd, path);
7799
+ try {
7800
+ return await work(target);
7801
+ } catch (error) {
7802
+ throw classifyErrno(error, {
7803
+ path: target,
7804
+ access,
7805
+ callerLine,
7806
+ env
7807
+ });
7808
+ }
7809
+ }
6919
7810
  return {
6920
- stdout: `${build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""})`}\n`,
6921
- stderr: "",
6922
- exitCode: SUCCESS_EXIT_CODE
7811
+ exists: (path) => at("read", path, (target) => files.exists(target)),
7812
+ write: (path, data) => at("write", path, (target) => files.write(target, data)),
7813
+ read: (path) => at("read", path, (target) => files.read(target)),
7814
+ list: (folder) => at("read", folder, async (target) => (await files.list(target)).filter((name) => !TEMP_FILE.test(name)).sort()),
7815
+ remove: (path) => at("write", path, (target) => files.remove(target)),
7816
+ mkdir: (path, options) => at("write", path, (target) => files.mkdir(target, options))
6923
7817
  };
6924
7818
  }
6925
7819
  //#endregion
7820
+ //#region ../agent-cli/dist/package-root.js
7821
+ /**
7822
+ * The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
7823
+ * from `cwd`; the search's start when none holds one. The bin is resolved through links first,
7824
+ * since npm's global bin is a link to it.
7825
+ */
7826
+ function packageRootOf(bin, cwd) {
7827
+ const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
7828
+ for (let dir = start;; dir = dirname(dir)) {
7829
+ if (existsSync(join(dir, "package.json"))) return dir;
7830
+ if (dirname(dir) === dir) return start;
7831
+ }
7832
+ }
7833
+ //#endregion
6926
7834
  //#region ../agent-cli/dist/suggest.js
6927
7835
  const MAX_DISTANCE = 3;
6928
7836
  const MIN_SIMILARITY = .4;
@@ -7047,40 +7955,6 @@ function tokenize(words, known) {
7047
7955
  };
7048
7956
  }
7049
7957
  //#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
7958
  //#region ../agent-cli/dist/parse-input.js
7085
7959
  const NO_INPUT = object({});
7086
7960
  function knownFlags(spec) {
@@ -7112,10 +7986,14 @@ function optionValue(field, raw) {
7112
7986
  return field.type === "boolean" && raw === true && field.label === `--no-${field.name}` ? false : raw;
7113
7987
  }
7114
7988
  function converted(field, raw) {
7989
+ if (Array.isArray(raw)) return raw.map((element) => converted(field, element));
7115
7990
  if (field.type !== "number" || typeof raw !== "string" || raw.trim() === "") return raw;
7116
7991
  const number = Number(raw);
7117
7992
  return Number.isNaN(number) ? raw : number;
7118
7993
  }
7994
+ function variadicValue(field, values) {
7995
+ return values.length === 0 && !field.required ? void 0 : values;
7996
+ }
7119
7997
  function parse(schema, values) {
7120
7998
  const input = Object.fromEntries([...values].filter(([, raw]) => raw !== void 0).map(([field, raw]) => [field.key, converted(field, raw)]));
7121
7999
  return schema.safeParse(input);
@@ -7127,9 +8005,15 @@ function issueLines(result, values, howToPass, reported) {
7127
8005
  if (field === void 0) return [` ${issue.message}`];
7128
8006
  if (reported.includes(field)) return [];
7129
8007
  const raw = values.get(field);
7130
- return raw === void 0 ? [` ${field.label}: required, ${howToPass(field)}`] : [` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`];
8008
+ if (raw === void 0) return [` ${field.label}: required, ${howToPass(field)}`];
8009
+ return Array.isArray(raw) ? [operandsIssueLine(field, raw, issue, [...values.keys()].indexOf(field) + 1)] : [` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`];
7131
8010
  });
7132
8011
  }
8012
+ function operandsIssueLine(field, raw, issue, first) {
8013
+ const [, element] = issue.path;
8014
+ if (typeof element === "number") return ` ${field.label} ${JSON.stringify(raw[element])} (argument ${first + element}): ${issue.message}`;
8015
+ return raw.length === 0 ? ` ${field.label}: ${issue.message}, pass them as arguments from ${first}` : ` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`;
8016
+ }
7133
8017
  function unknownLine({ flag, suggestion }) {
7134
8018
  return ` unknown option '${flag}'${suggestion === void 0 ? "" : ` (did you mean ${suggestion}?)`}`;
7135
8019
  }
@@ -7137,7 +8021,7 @@ function invalidInput(commandLine, invocation, unknown, otherLines) {
7137
8021
  const renames = new Map(unknown.flatMap(({ flag, suggestion }) => suggestion === void 0 ? [] : [[flag, suggestion]]));
7138
8022
  const correctable = otherLines.length === 0 && renames.size === unknown.length;
7139
8023
  return new UsageError({
7140
- code: "INVALID_INPUT",
8024
+ code: INVALID_INPUT,
7141
8025
  message: [
7142
8026
  `invalid input for \`${commandLine}\``,
7143
8027
  ...unknown.map(unknownLine),
@@ -7153,7 +8037,8 @@ function parseInput(spec, words, invocation) {
7153
8037
  const { operands, unknownOptions, values } = tokenize(words, known);
7154
8038
  if (values.get(CONFIRM_FLAG.flag) === true) throw invalidInputError(spec.commandLine, `option '${CONFIRM_FLAG.usage}' argument missing`);
7155
8039
  const missing = [...optionFields, ...pageFields].filter((field) => field.type !== "boolean" && values.get(field.label) === true);
7156
- const argValues = new Map(argFields.map((field, index) => [field, operands[index]?.value]));
8040
+ const argValues = new Map(argFields.map((field, index) => [field, field.variadic ? variadicValue(field, operands.slice(index).filter((operand) => !operand.afterUnknownOption).map((operand) => operand.value)) : operands[index]?.value]));
8041
+ const extraOperands = argFields.some((field) => field.variadic) ? [] : operands.slice(argFields.length);
7157
8042
  const valuesOf = (fields) => new Map(fields.map((field) => [field, missing.includes(field) ? void 0 : optionValue(field, values.get(field.label))]));
7158
8043
  const optionValues = valuesOf(optionFields);
7159
8044
  const pageValues = valuesOf(pageFields);
@@ -7163,7 +8048,7 @@ function parseInput(spec, words, invocation) {
7163
8048
  const unknown = unknownOptionsOf(unknownOptions, candidateFlags(known));
7164
8049
  const otherLines = [
7165
8050
  ...missing.map((field) => ` ${field.label}: missing value, pass ${field.usage}`),
7166
- ...operands.slice(argFields.length).filter((operand) => !operand.afterUnknownOption).map((operand) => ` unexpected argument ${JSON.stringify(operand.value)}`),
8051
+ ...extraOperands.filter((operand) => !operand.afterUnknownOption).map((operand) => ` unexpected argument ${JSON.stringify(operand.value)}`),
7167
8052
  ...issueLines(args, argValues, (field) => `pass it as argument ${argFields.indexOf(field) + 1}`, []),
7168
8053
  ...issueLines(options, optionValues, (field) => `pass ${field.usage}`, missing),
7169
8054
  ...issueLines(page, pageValues, (field) => `pass ${field.usage}`, missing)
@@ -7190,6 +8075,8 @@ function parseCommand({ spec, words, json }, invocation) {
7190
8075
  json,
7191
8076
  dryRun: values.get(DRY_RUN_FLAG.flag) === true,
7192
8077
  confirm: typeof confirm === "string" ? confirm : void 0,
8078
+ yes: values.get(YES_FLAG.flag) === true,
8079
+ force: values.get(FORCE_FLAG.flag) === true,
7193
8080
  page,
7194
8081
  fields: spec.list === void 0 ? void 0 : selectedFields(values, spec.list.rowFields, spec.commandLine)
7195
8082
  }
@@ -7244,6 +8131,7 @@ function walk(node, words, json, invocation) {
7244
8131
  return {
7245
8132
  kind: "usage error",
7246
8133
  error: unknownOptionError(node.commandLine, invocation, first, suggestions),
8134
+ node,
7247
8135
  json
7248
8136
  };
7249
8137
  }
@@ -7254,6 +8142,7 @@ function unknownCommand(group, name, json) {
7254
8142
  return {
7255
8143
  kind: "usage error",
7256
8144
  error: unknownCommandError(group.commandLine, name, suggestions),
8145
+ node: group,
7257
8146
  json
7258
8147
  };
7259
8148
  }
@@ -7269,19 +8158,102 @@ function route(root, invocation) {
7269
8158
  return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
7270
8159
  }
7271
8160
  //#endregion
8161
+ //#region ../agent-cli/dist/secrets.js
8162
+ function dotenvPath(cliName, env) {
8163
+ const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
8164
+ return join(config, cliName, ".env");
8165
+ }
8166
+ function readDotenv(path, env, callerLine) {
8167
+ let text;
8168
+ try {
8169
+ text = readFileSync(path, "utf8");
8170
+ } catch (error) {
8171
+ if (isMissingFile(error)) return {};
8172
+ throw classifyErrno(error, {
8173
+ path,
8174
+ access: "read",
8175
+ callerLine,
8176
+ env
8177
+ });
8178
+ }
8179
+ return parseEnv(text);
8180
+ }
8181
+ /** Looks `name` up in the env, then in `$XDG_CONFIG_HOME/<cli>/.env`; an empty value counts as unset. */
8182
+ function secretOf({ cliName, env, callerLine }, name) {
8183
+ const fromEnv = env[name];
8184
+ if (fromEnv) return {
8185
+ value: fromEnv,
8186
+ origin: { kind: "env" }
8187
+ };
8188
+ const path = dotenvPath(cliName, env);
8189
+ const fromFile = readDotenv(path, env, callerLine)[name];
8190
+ return fromFile ? {
8191
+ value: fromFile,
8192
+ origin: {
8193
+ kind: "file",
8194
+ path
8195
+ }
8196
+ } : void 0;
8197
+ }
8198
+ //#endregion
8199
+ //#region ../agent-cli/dist/spill.js
8200
+ const STDOUT_BUDGET_CHARS = 3e4;
8201
+ const SPILL_RETENTION_MS = 6048e5;
8202
+ const PREVIEW_CHARS = 2e3;
8203
+ const SPILL_FILE = /^\d{4}-\d\d-\d\dT\d\d-\d\d-\d\d\.\d{3}Z-.+\.(json|txt)$/;
8204
+ function spillName(now, commandName, json) {
8205
+ return `${new Date(now).toISOString().replaceAll(":", "-")}-${commandName}.${json ? "json" : "txt"}`;
8206
+ }
8207
+ async function pruneOne(path, cutoff) {
8208
+ try {
8209
+ if ((await stat(path)).mtimeMs < cutoff) await rm(path, { force: true });
8210
+ } catch (error) {
8211
+ if (!isMissingFile(error)) throw error;
8212
+ }
8213
+ }
8214
+ async function pruneOld(folder, cutoff) {
8215
+ const names = (await readdir(folder)).filter((name) => SPILL_FILE.test(name));
8216
+ await Promise.all(names.map((name) => pruneOne(join(folder, name), cutoff)));
8217
+ }
8218
+ async function prepare(folder, cutoff, target) {
8219
+ try {
8220
+ await mkdir(folder, { recursive: true });
8221
+ await pruneOld(folder, cutoff);
8222
+ } catch (error) {
8223
+ throw classifyErrno(error, {
8224
+ path: folder,
8225
+ access: "write",
8226
+ callerLine: target.callerLine,
8227
+ env: target.env
8228
+ });
8229
+ }
8230
+ }
8231
+ function tempRoot(env) {
8232
+ const { TMPDIR } = env;
8233
+ return TMPDIR === void 0 || TMPDIR === "" ? tmpdir() : TMPDIR;
8234
+ }
8235
+ function spillTo(target) {
8236
+ return async (printed, json) => {
8237
+ if (printed.length <= STDOUT_BUDGET_CHARS) return printed;
8238
+ const now = target.clock.now();
8239
+ const folder = join(tempRoot(target.env), target.cliName);
8240
+ await prepare(folder, now - SPILL_RETENTION_MS, target);
8241
+ const file = join(folder, spillName(now, target.commandName, json));
8242
+ await target.files.write(file, printed);
8243
+ const bytes = Buffer.byteLength(printed);
8244
+ const preview = printed.slice(0, PREVIEW_CHARS);
8245
+ return json ? `${JSON.stringify({
8246
+ file,
8247
+ bytes,
8248
+ preview
8249
+ })}\n` : `file: ${file}\nbytes: ${bytes}\npreview:\n${preview}\n`;
8250
+ };
8251
+ }
8252
+ //#endregion
7272
8253
  //#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
8254
  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;
8255
+ if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
8256
+ return env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0" || stdoutIsTTY && !isAgent(env);
7285
8257
  }
7286
8258
  function sgr(open, close) {
7287
8259
  return (text) => `\u001b[${open}m${text}\u001b[${close}m`;
@@ -7308,7 +8280,7 @@ function styleFor(env, stdoutIsTTY) {
7308
8280
  }
7309
8281
  //#endregion
7310
8282
  //#region ../agent-cli/dist/run-cli.js
7311
- async function runCommand(routed, { base, signal, format: baseFormat }) {
8283
+ async function runCommand(routed, { base, writeStdout, signal, format: baseFormat, ports, clock, files, spawn, projectFile }) {
7312
8284
  const { command, commandLine } = routed.spec;
7313
8285
  const format = {
7314
8286
  ...baseFormat,
@@ -7316,19 +8288,70 @@ async function runCommand(routed, { base, signal, format: baseFormat }) {
7316
8288
  };
7317
8289
  const { invocation } = format;
7318
8290
  let parsed;
8291
+ let project;
7319
8292
  try {
7320
8293
  parsed = parseCommand(routed, invocation);
8294
+ project = projectFile === void 0 ? void 0 : projectOf(projectFile, base.cwd);
7321
8295
  } catch (error) {
7322
8296
  return errorResult(error, format);
7323
8297
  }
7324
8298
  const { input, flags } = parsed;
7325
- const control = new RunControl(commandLine, signal);
8299
+ const control = new RunControl({
8300
+ commandLine,
8301
+ outer: signal,
8302
+ clock,
8303
+ progress: base.progress,
8304
+ runDeadline: command.deadline === void 0 ? void 0 : {
8305
+ ms: command.deadline.ms,
8306
+ hint: command.deadline.resume(input)
8307
+ }
8308
+ });
8309
+ let delivered;
8310
+ const callerLine = commandLineOf(invocation);
8311
+ const classified = classifiedFiles(files, {
8312
+ cwd: base.cwd,
8313
+ env: base.env,
8314
+ callerLine
8315
+ });
8316
+ const spill = spillTo({
8317
+ cliName: format.cliName,
8318
+ commandName: command.name,
8319
+ env: base.env,
8320
+ clock,
8321
+ files: classified,
8322
+ callerLine
8323
+ });
8324
+ const portsBase = {
8325
+ cwd: base.cwd,
8326
+ env: base.env,
8327
+ packageRoot: base.packageRoot,
8328
+ secret: base.secret,
8329
+ signal: control.signal,
8330
+ clock,
8331
+ files: classified,
8332
+ spawn: contextSpawn(spawn, {
8333
+ cwd: base.cwd,
8334
+ env: base.env,
8335
+ signal: control.signal
8336
+ }, callerLine)
8337
+ };
8338
+ const builtPorts = once(() => ports(portsBase));
7326
8339
  const ctx = {
7327
8340
  ...base,
7328
- signal: control.signal,
8341
+ ...portsBase,
7329
8342
  deadline: (ms) => control.deadline(ms),
7330
- runToEnd: (work) => control.runToEnd(work)
8343
+ runToEnd: (name, work) => control.runToEnd(name, work),
8344
+ get ports() {
8345
+ return builtPorts();
8346
+ },
8347
+ project,
8348
+ force: flags.force,
8349
+ poll: (check, { intervalMs }) => poll(clock, check, intervalMs, control.signal),
8350
+ deliver: (data) => {
8351
+ delivered = deliveredOutcome(command, data);
8352
+ }
7331
8353
  };
8354
+ let served = false;
7332
8355
  let settled;
7333
8356
  try {
7334
8357
  settled = {
@@ -7336,24 +8359,53 @@ async function runCommand(routed, { base, signal, format: baseFormat }) {
7336
8359
  outcome: await control.settle(execute(command, input, ctx, {
7337
8360
  dryRun: flags.dryRun,
7338
8361
  confirm: flags.confirm,
8362
+ yes: flags.yes,
7339
8363
  page: flags.page,
7340
8364
  invocation,
7341
- commandLine
8365
+ commandLine,
8366
+ started: async (outcome) => {
8367
+ writeStdout(await startedOutput(outcome, format, spill));
8368
+ served = true;
8369
+ }
7342
8370
  }))
7343
8371
  };
7344
8372
  } catch (error) {
8373
+ if (served && control.signal.aborted) return STOPPED_RESULT;
7345
8374
  settled = {
7346
8375
  ok: false,
7347
8376
  error: control.errorAfter(error),
7348
- render: renderOf(command)
8377
+ delivered
7349
8378
  };
7350
8379
  }
7351
8380
  return commandResult(settled, {
7352
8381
  ...format,
7353
8382
  fields: flags.fields
7354
- });
8383
+ }, spill);
8384
+ }
8385
+ /** `build` runs on the first call; later calls give its value or throw its error again. */
8386
+ function once(build) {
8387
+ let outcome;
8388
+ return () => {
8389
+ if (outcome === void 0) try {
8390
+ outcome = {
8391
+ ok: true,
8392
+ value: build()
8393
+ };
8394
+ } catch (error) {
8395
+ outcome = {
8396
+ ok: false,
8397
+ error
8398
+ };
8399
+ }
8400
+ if (outcome.ok) return outcome.value;
8401
+ throw outcome.error;
8402
+ };
7355
8403
  }
7356
- async function runWithProgress(cli, argv, options, writeProgress) {
8404
+ function portsOf(cli, replacement) {
8405
+ if (replacement !== void 0) return () => replacement;
8406
+ return cli.ports ?? (() => ({}));
8407
+ }
8408
+ async function runWithProgress(cli, argv, options, write) {
7357
8409
  const format = {
7358
8410
  cliName: cli.name,
7359
8411
  env: options.env,
@@ -7362,40 +8414,55 @@ async function runWithProgress(cli, argv, options, writeProgress) {
7362
8414
  cliName: cli.name,
7363
8415
  argv
7364
8416
  },
7365
- json: false
8417
+ json: false,
8418
+ errors: cli.errors ?? {},
8419
+ stop: options.signal
7366
8420
  };
8421
+ const builtIns = cli.commands === void 0 ? {} : builtInCommands(cli, options.bin);
7367
8422
  const root = cli.commands === void 0 ? commandSpec(cli.name, cli.command) : groupSpec(cli.name, {
7368
8423
  ...cli.commands,
7369
- ...builtInCommands(cli, options.bin)
8424
+ ...builtIns
7370
8425
  });
7371
8426
  const routed = route(root, format.invocation);
8427
+ const misused = (node) => isAgent(options.env) ? {
8428
+ ...format,
8429
+ usageHelp: textHelp(node, root, cli, format.style)
8430
+ } : format;
7372
8431
  switch (routed.kind) {
7373
- case "version": return versionResult(cli);
8432
+ case "version": return versionResult(cli, packageRootOf(options.bin, options.cwd));
7374
8433
  case "help": return helpResult(routed.node, root, cli, {
7375
8434
  ...format,
7376
8435
  json: routed.json
7377
8436
  });
7378
8437
  case "usage error": return errorResult(routed.error, {
7379
- ...format,
8438
+ ...misused(routed.node),
7380
8439
  json: routed.json
7381
8440
  });
7382
8441
  case "command": return runCommand(routed, {
7383
8442
  base: {
7384
8443
  cwd: options.cwd,
7385
8444
  env: options.env,
7386
- progress: (message) => writeProgress(`${message}\n`)
8445
+ packageRoot: packageRootOf(options.bin, options.cwd),
8446
+ progress: (message) => write.stderr(`${message}\n`),
8447
+ secret: (name) => secretOf({
8448
+ cliName: cli.name,
8449
+ env: options.env,
8450
+ callerLine: commandLineOf(format.invocation)
8451
+ }, name)
7387
8452
  },
8453
+ writeStdout: (text) => write.stdout(text),
7388
8454
  signal: options.signal,
7389
- format
8455
+ format: misused(routed.spec),
8456
+ ports: portsOf(cli, options.ports),
8457
+ clock: options.clock ?? realClock,
8458
+ files: options.files ?? realFiles(),
8459
+ spawn: options.spawn ?? realSpawn,
8460
+ projectFile: Object.values(builtIns).includes(routed.spec.command) ? void 0 : cli.project
7390
8461
  });
7391
8462
  }
7392
8463
  }
7393
8464
  //#endregion
7394
8465
  //#region ../agent-cli/dist/run-process.js
7395
- const SIGNAL_EXIT_CODES = {
7396
- SIGINT: 130,
7397
- SIGTERM: 143
7398
- };
7399
8466
  function written(stream, text) {
7400
8467
  return new Promise((resolve) => stream.write(text, () => resolve()));
7401
8468
  }
@@ -7404,7 +8471,7 @@ async function runProcess(cli) {
7404
8471
  const onSignal = (signal) => {
7405
8472
  if (stop.signal.aborted) process.exit(SIGNAL_EXIT_CODES[signal]);
7406
8473
  process.stderr.write("\nstopping… (Ctrl-C again to quit now)\n");
7407
- stop.abort();
8474
+ stop.abort(signal);
7408
8475
  };
7409
8476
  process.on("SIGINT", onSignal);
7410
8477
  process.on("SIGTERM", onSignal);
@@ -7415,7 +8482,10 @@ async function runProcess(cli) {
7415
8482
  stdoutIsTTY: process.stdout.isTTY === true,
7416
8483
  signal: stop.signal,
7417
8484
  bin: process.argv[1]
7418
- }, (line) => process.stderr.write(line));
8485
+ }, {
8486
+ stdout: (text) => process.stdout.write(text),
8487
+ stderr: (text) => process.stderr.write(text)
8488
+ });
7419
8489
  await written(process.stdout, result.stdout);
7420
8490
  await written(process.stderr, result.stderr);
7421
8491
  process.exitCode = result.exitCode;
@@ -7428,11 +8498,13 @@ async function runProcess(cli) {
7428
8498
  //#endregion
7429
8499
  //#region src/build.ts
7430
8500
  const build = {
7431
- "sha": "f35d0e3",
7432
- "dirty": false
8501
+ "sha": "a7760d0",
8502
+ "dirty": false,
8503
+ "builtAt": "2026-10-19T10:55:24.637Z"
7433
8504
  };
7434
8505
  //#endregion
7435
8506
  //#region src/steps.ts
8507
+ const stepErrors = defineErrors({ STEP_FAILED: { class: "operator" } });
7436
8508
  const INSTALL = [{
7437
8509
  command: "pnpm",
7438
8510
  args: ["install"]
@@ -7501,11 +8573,9 @@ function tail(output) {
7501
8573
  return output.trimEnd().split(/\r?\n/).slice(-20).join("\n");
7502
8574
  }
7503
8575
  function stepFailed(problem, left, cwd, cause) {
7504
- return new CliError({
7505
- code: "STEP_FAILED",
8576
+ return cliError(stepErrors, "STEP_FAILED", {
7506
8577
  message: problem,
7507
- hint: `fix the problem, then run in ${cwd}: ${left.map(stepLine).join(" && ")}`,
7508
- retryable: false
8578
+ hint: `fix the problem, then run in ${cwd}: ${left.map(stepLine).join(" && ")}`
7509
8579
  }, { cause });
7510
8580
  }
7511
8581
  /** Runs each step in `cwd` in order, capturing its output so only the Runtime writes to stdout. */
@@ -7559,9 +8629,10 @@ async function writeTemplate(root, files, values) {
7559
8629
  }
7560
8630
  //#endregion
7561
8631
  //#region src/version.ts
7562
- const version = "0.2.1";
8632
+ const version = "0.4.0";
7563
8633
  //#endregion
7564
8634
  //#region src/create.ts
8635
+ const { defineCommand } = agentCli();
7565
8636
  const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?([a-z0-9][a-z0-9._-]*)$/;
7566
8637
  function errorCode(error) {
7567
8638
  return error instanceof Error && "code" in error ? error.code : void 0;
@@ -7582,64 +8653,69 @@ async function ensureFree(path) {
7582
8653
  hint: "run `create-agent-cli <name>` with a name whose directory is free, or empty it first"
7583
8654
  });
7584
8655
  }
8656
+ const create = defineCommand({
8657
+ name: "create",
8658
+ description: "Create a new Agent CLI project in ./<name>",
8659
+ examples: [{
8660
+ command: "create-agent-cli weather --description \"Weather forecasts\"",
8661
+ note: "create ./weather, install dependencies and make the first commit"
8662
+ }],
8663
+ effect: "write",
8664
+ 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") }),
8665
+ options: object({
8666
+ description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
8667
+ install: boolean().default(true).describe("run pnpm install"),
8668
+ git: boolean().default(true).describe("create a git repository with an initial commit")
8669
+ }),
8670
+ plan: async ({ name, description, install, git }, ctx) => {
8671
+ const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
8672
+ const path = resolve(ctx.cwd, binName);
8673
+ await ensureFree(path);
8674
+ const values = {
8675
+ packageName: name,
8676
+ binName,
8677
+ description,
8678
+ debugVariable: debugVariable(binName),
8679
+ runtimeVersion: version
8680
+ };
8681
+ return {
8682
+ path,
8683
+ values,
8684
+ files: await templateFiles(values),
8685
+ steps: stepsFor({
8686
+ install,
8687
+ git
8688
+ }),
8689
+ install
8690
+ };
8691
+ },
8692
+ apply: async (plan, ctx) => {
8693
+ await mkdir(plan.path, { recursive: true });
8694
+ await writeTemplate(plan.path, plan.files, plan.values);
8695
+ await runSteps(plan.steps, plan.path, ctx);
8696
+ return {
8697
+ path: plan.path,
8698
+ next: `cd ${plan.values.binName} && pnpm ${plan.install ? "test" : "install"}`
8699
+ };
8700
+ },
8701
+ render: ({ path, next }) => `Created ${path}\nnext: ${next}`,
8702
+ renderPlan: (plan) => [
8703
+ `Would create ${plan.path}:`,
8704
+ ...plan.files.map((file) => ` create ${file.target}`),
8705
+ ...plan.steps.map((step) => ` run ${stepLine(step)}`)
8706
+ ].join("\n")
8707
+ });
8708
+ //#endregion
8709
+ //#region src/cli.ts
8710
+ const { defineCli } = agentCli();
7585
8711
  //#endregion
7586
8712
  //#region src/bin.ts
7587
8713
  await runProcess(defineCli({
7588
8714
  name: "create-agent-cli",
7589
8715
  version,
7590
8716
  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
- })
8717
+ errors: stepErrors,
8718
+ command: create
7643
8719
  }));
7644
8720
  //#endregion
7645
8721
  export {};