@duyquangnvx/create-agent-cli 0.17.0 → 0.18.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
@@ -2,8 +2,8 @@
2
2
  import { parseArgs, parseEnv } from "node:util";
3
3
  import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
  import { constants, existsSync, readFileSync, realpathSync } from "node:fs";
5
- import { access, chmod, link, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
6
5
  import { constants as constants$1, homedir, tmpdir } from "node:os";
6
+ import { access, chmod, link, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
7
7
  import { spawn } from "node:child_process";
8
8
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
9
9
  import { fileURLToPath } from "node:url";
@@ -139,7 +139,7 @@ const allowsEval = /* @__PURE__*/ cached(() => {
139
139
  return false;
140
140
  }
141
141
  });
142
- function isPlainObject(o) {
142
+ function isPlainObject$1(o) {
143
143
  if (isObject(o) === false) return false;
144
144
  const ctor = o.constructor;
145
145
  if (ctor === void 0) return true;
@@ -150,7 +150,7 @@ function isPlainObject(o) {
150
150
  return true;
151
151
  }
152
152
  function shallowClone(o) {
153
- if (isPlainObject(o)) return { ...o };
153
+ if (isPlainObject$1(o)) return { ...o };
154
154
  if (Array.isArray(o)) return [...o];
155
155
  if (o instanceof Map) return new Map(o);
156
156
  if (o instanceof Set) return new Set(o);
@@ -238,7 +238,7 @@ function omit(schema, mask) {
238
238
  }));
239
239
  }
240
240
  function extend(schema, shape) {
241
- if (!isPlainObject(shape)) throw new Error("Invalid input to extend: expected a plain object");
241
+ if (!isPlainObject$1(shape)) throw new Error("Invalid input to extend: expected a plain object");
242
242
  const checks = schema._zod.def.checks;
243
243
  if (checks && checks.length > 0) {
244
244
  const existingShape = sourceShape(schema);
@@ -253,7 +253,7 @@ function extended(schema, shape) {
253
253
  return newShape;
254
254
  }
255
255
  function safeExtend(schema, shape) {
256
- if (!isPlainObject(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
256
+ if (!isPlainObject$1(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
257
257
  return clone(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));
258
258
  }
259
259
  function merge(a, b) {
@@ -2338,7 +2338,7 @@ function mergeValues(a, b) {
2338
2338
  valid: true,
2339
2339
  data: a
2340
2340
  };
2341
- if (isPlainObject(a) && isPlainObject(b)) {
2341
+ if (isPlainObject$1(a) && isPlainObject$1(b)) {
2342
2342
  const bKeys = Object.keys(b);
2343
2343
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
2344
2344
  const newObj = {
@@ -2431,7 +2431,7 @@ const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
2431
2431
  memo?.attach(inst);
2432
2432
  inst._zod.parse = (payload, ctx) => {
2433
2433
  const input = payload.value;
2434
- if (!isPlainObject(input)) {
2434
+ if (!isPlainObject$1(input)) {
2435
2435
  payload.issues.push({
2436
2436
  expected: "record",
2437
2437
  code: "invalid_type",
@@ -4863,7 +4863,7 @@ const parse$1 = /* @__PURE__ */ _parse(ZodRealError);
4863
4863
  const parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);
4864
4864
  const safeParse = /* @__PURE__ */ _safeParse(ZodRealError);
4865
4865
  const safeParseAsync = /* @__PURE__ */ _safeParseAsync(ZodRealError);
4866
- const encode = /* @__PURE__ */ _encode(ZodRealError);
4866
+ const encode$1 = /* @__PURE__ */ _encode(ZodRealError);
4867
4867
  const decode = /* @__PURE__ */ _decode(ZodRealError);
4868
4868
  const encodeAsync = /* @__PURE__ */ _encodeAsync(ZodRealError);
4869
4869
  const decodeAsync = /* @__PURE__ */ _decodeAsync(ZodRealError);
@@ -5015,7 +5015,7 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
5015
5015
  return validateAsync$1(this, data, params);
5016
5016
  },
5017
5017
  encode: function _encode(data, params) {
5018
- return encode(this, data, params, { callee: _encode });
5018
+ return encode$1(this, data, params, { callee: _encode });
5019
5019
  },
5020
5020
  decode: function _decode(data, params) {
5021
5021
  return decode(this, data, params, { callee: _decode });
@@ -5774,21 +5774,196 @@ async function poll(clock, check, intervalMs, signal) {
5774
5774
  }
5775
5775
  }
5776
5776
  //#endregion
5777
+ //#region ../agent-cli/dist/command-line/tokenize.js
5778
+ const NEGATIVE_NUMBER = /^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/;
5779
+ function isOptionLike(word) {
5780
+ return word !== void 0 && word.length > 1 && word[0] === "-" && !NEGATIVE_NUMBER.test(word);
5781
+ }
5782
+ function parseArgsOptions(known) {
5783
+ return Object.fromEntries(known.map(({ flag, takes }) => [flag.slice(2), { type: takes === "value" ? "string" : "boolean" }]));
5784
+ }
5785
+ function flagItem(token, { flag, takes }, words) {
5786
+ if (takes !== "optional value" || token.inlineValue === true) return {
5787
+ kind: "flag",
5788
+ flag,
5789
+ value: token.value ?? true
5790
+ };
5791
+ const took = token.index + 1;
5792
+ const next = words[took];
5793
+ return next === void 0 || isOptionLike(next) ? {
5794
+ kind: "flag",
5795
+ flag,
5796
+ value: true
5797
+ } : {
5798
+ kind: "flag",
5799
+ flag,
5800
+ value: next,
5801
+ took
5802
+ };
5803
+ }
5804
+ function itemOf(token, words, byFlag) {
5805
+ const word = words[token.index] ?? "";
5806
+ if (token.kind === "option-terminator") return { kind: "end" };
5807
+ if (token.kind === "positional" || NEGATIVE_NUMBER.test(word)) return {
5808
+ kind: "operand",
5809
+ value: word
5810
+ };
5811
+ const flag = byFlag.get(token.rawName);
5812
+ return flag === void 0 || flag.takes === "nothing" && token.inlineValue === true ? {
5813
+ kind: "unknown option",
5814
+ word
5815
+ } : flagItem(token, flag, words);
5816
+ }
5817
+ /** Splits a Command's words into operands, unknown options and the values of known flags. */
5818
+ function tokenize(words, known) {
5819
+ const byFlag = new Map(known.map((flag) => [flag.flag, flag]));
5820
+ const { tokens } = parseArgs({
5821
+ args: [...words],
5822
+ options: parseArgsOptions(known),
5823
+ strict: false,
5824
+ allowPositionals: true,
5825
+ tokens: true
5826
+ });
5827
+ const operands = [];
5828
+ const unknownOptions = [];
5829
+ const occurrences = /* @__PURE__ */ new Map();
5830
+ const done = /* @__PURE__ */ new Set();
5831
+ let literal = false;
5832
+ let afterUnknownOption = false;
5833
+ for (const token of tokens) {
5834
+ if (done.has(token.index)) continue;
5835
+ done.add(token.index);
5836
+ const item = itemOf(token, words, byFlag);
5837
+ if (item.kind === "end") literal = true;
5838
+ else if (item.kind === "operand") {
5839
+ operands.push({
5840
+ value: item.value,
5841
+ afterUnknownOption: !literal && afterUnknownOption
5842
+ });
5843
+ afterUnknownOption = false;
5844
+ } else if (item.kind === "unknown option") {
5845
+ unknownOptions.push(item.word);
5846
+ afterUnknownOption = true;
5847
+ } else {
5848
+ occurrences.set(item.flag, [...occurrences.get(item.flag) ?? [], item.value]);
5849
+ if (item.took !== void 0) done.add(item.took);
5850
+ }
5851
+ }
5852
+ return {
5853
+ operands,
5854
+ unknownOptions,
5855
+ values: new Map([...occurrences].map(([flag, all]) => [flag, all[all.length - 1] ?? true])),
5856
+ occurrences
5857
+ };
5858
+ }
5859
+ const SHELL_SAFE = /^[\w@%+=:,./-]+$/;
5860
+ function quoted$1(token) {
5861
+ return SHELL_SAFE.test(token) ? token : `'${token.replaceAll("'", `'\\''`)}'`;
5862
+ }
5863
+ function pasteable(runAs, tokens, stdinArg) {
5864
+ const line = [runAs, ...tokens.map(quoted$1)].join(" ");
5865
+ return stdinArg === void 0 ? line : `${line} < <${stdinArg} file>`;
5866
+ }
5867
+ function split(argv) {
5868
+ const end = argv.indexOf("--");
5869
+ return end === -1 ? {
5870
+ options: [...argv],
5871
+ operands: []
5872
+ } : {
5873
+ options: argv.slice(0, end),
5874
+ operands: argv.slice(end)
5875
+ };
5876
+ }
5877
+ function tookValue(takes, next) {
5878
+ return takes === "value" || takes === "optional value" && next !== void 0 && !isOptionLike(next);
5879
+ }
5880
+ function without(flag, takes, tokens) {
5881
+ const kept = [];
5882
+ for (let index = 0; index < tokens.length; index += 1) {
5883
+ const token = tokens[index] ?? "";
5884
+ if (token === flag) {
5885
+ if (tookValue(takes, tokens[index + 1])) index += 1;
5886
+ } else if (!token.startsWith(`${flag}=`)) kept.push(token);
5887
+ }
5888
+ return kept;
5889
+ }
5890
+ /**
5891
+ * The caller's words after the CLI's name, with each flag in `remove` dropped along with its value
5892
+ * and the words in `add` placed before any `--`.
5893
+ */
5894
+ function changedArgv({ argv, flags }, { add = [], remove = [] } = {}) {
5895
+ const { options, operands } = split(argv);
5896
+ const takes = new Map(flags.map((known) => [known.flag, known.takes]));
5897
+ return [
5898
+ ...remove.reduce((tokens, flag) => without(flag, takes.get(flag) ?? "nothing", tokens), options),
5899
+ ...add,
5900
+ ...operands
5901
+ ];
5902
+ }
5903
+ /** The caller's command line as typed, ready to paste, changed as `changedArgv` does, the stdin arg's file redirected in. */
5904
+ function commandLineOf(invocation, change) {
5905
+ return pasteable(invocation.runAs, changedArgv(invocation, change), invocation.stdinArg);
5906
+ }
5907
+ function renamed(token, renames) {
5908
+ const [flag = token, ...value] = token.split("=");
5909
+ const name = renames.get(flag);
5910
+ if (name === void 0) return token;
5911
+ return value.length === 0 ? name : `${name}=${value.join("=")}`;
5912
+ }
5913
+ /** The caller's command line, ready to paste, with each flag in `renames` typed as its new name. */
5914
+ function commandLineRenaming({ runAs, argv, stdinArg }, renames) {
5915
+ const { options, operands } = split(argv);
5916
+ return pasteable(runAs, [...options.map((token) => renamed(token, renames)), ...operands], stdinArg);
5917
+ }
5918
+ //#endregion
5919
+ //#region ../agent-cli/dist/next-step.js
5920
+ function wholeLine(line) {
5921
+ return {
5922
+ kind: "line",
5923
+ line
5924
+ };
5925
+ }
5926
+ function isWords(step) {
5927
+ return Array.isArray(step);
5928
+ }
5929
+ /** The words after the Run-as, or `undefined` for a line given whole. */
5930
+ function wordsOf(step) {
5931
+ if (isWords(step)) return step;
5932
+ return step.kind === "rerun" ? step.words : void 0;
5933
+ }
5934
+ /** The Next step as one shell-quoted line starting with the Run-as, or the whole line as given. */
5935
+ function lineOf(step, runAs) {
5936
+ if (isWords(step)) return pasteable(runAs, step);
5937
+ return step.kind === "rerun" ? pasteable(runAs, step.words, step.stdinArg) : step.line;
5938
+ }
5939
+ //#endregion
5777
5940
  //#region ../agent-cli/dist/errors.js
5778
5941
  var CliError = class extends Error {
5779
5942
  code;
5780
5943
  hint;
5781
5944
  details;
5782
5945
  retryAt;
5783
- constructor({ code, message, hint, details, retryAt }, options) {
5946
+ next;
5947
+ constructor({ code, message, hint, details, retryAt, next = [] }, options) {
5784
5948
  super(message, options);
5785
5949
  this.name = "CliError";
5786
5950
  this.code = code;
5787
5951
  this.hint = hint;
5788
5952
  this.details = details;
5789
5953
  this.retryAt = retryAt;
5954
+ this.next = next;
5790
5955
  }
5791
5956
  };
5957
+ var RuntimeError = class extends CliError {
5958
+ constructor({ next, ...fields }, options) {
5959
+ super(fields, options);
5960
+ this.next = next;
5961
+ }
5962
+ };
5963
+ /** A `CliError` whose Next steps may hold a line given whole, which only the Runtime builds. */
5964
+ function runtimeError(fields, options) {
5965
+ return new RuntimeError(fields, options);
5966
+ }
5792
5967
  /** The caller called the Agent CLI wrong; the same call can't succeed. */
5793
5968
  var UsageError = class extends CliError {
5794
5969
  constructor(fields) {
@@ -5834,21 +6009,28 @@ function bugHint(format) {
5834
6009
  return bugs === void 0 ? rerun : `${rerun}, then report it at ${bugs}`;
5835
6010
  }
5836
6011
  function fieldsOf$1(error, format) {
5837
- if (error instanceof CliError) {
5838
- const { code, message, hint, details, retryAt } = error;
5839
- return {
5840
- code,
5841
- message,
5842
- hint: format.isBug(error) ? bugHint(format) : hint,
5843
- details,
5844
- retryAt
5845
- };
5846
- }
5847
- return {
6012
+ if (!(error instanceof CliError)) return {
5848
6013
  code: INTERNAL,
5849
6014
  message: messageOf(error),
5850
6015
  hint: bugHint(format)
5851
6016
  };
6017
+ const { code, message, hint, details, retryAt, next } = error;
6018
+ if (format.isBug(error)) return {
6019
+ code,
6020
+ message,
6021
+ hint: bugHint(format),
6022
+ details,
6023
+ retryAt
6024
+ };
6025
+ const lines = next.map((step) => lineOf(step, format.runAs));
6026
+ return {
6027
+ code,
6028
+ message,
6029
+ hint,
6030
+ details,
6031
+ retryAt,
6032
+ ...lines.length === 0 ? {} : { next: lines }
6033
+ };
5852
6034
  }
5853
6035
  function causesOf(error) {
5854
6036
  return error instanceof Error && error.cause !== void 0 ? [error.cause, ...causesOf(error.cause)] : [];
@@ -5884,7 +6066,8 @@ function errorLines(error, format) {
5884
6066
  const { style } = format;
5885
6067
  const label = style.red(format.isBug(error) ? "internal error:" : "error:");
5886
6068
  const between = blocks.map((block) => `${block}\n`).join("");
5887
- return `${label} ${fields.message}\n${between}${style.cyan("hint:")} ${fields.hint}\n`;
6069
+ const runs = (fields.next ?? []).map((line) => `${style.cyan("run:")} ${line}\n`).join("");
6070
+ return `${label} ${fields.message}\n${between}${style.cyan("hint:")} ${fields.hint}\n${runs}`;
5888
6071
  }
5889
6072
  function formatRuntimeError(error, format) {
5890
6073
  const errors = errorsOf(error);
@@ -5898,7 +6081,7 @@ function formatRuntimeError(error, format) {
5898
6081
  return errors.map((each) => errorLines(each, format)).join("");
5899
6082
  }
5900
6083
  //#endregion
5901
- //#region ../agent-cli/dist/stdin.js
6084
+ //#region ../agent-cli/dist/context/stdin.js
5902
6085
  const STDIN_META = "stdin";
5903
6086
  function isStdinSchema(schema) {
5904
6087
  return globalRegistry$1.get(schema)?.[STDIN_META] === true;
@@ -5972,7 +6155,7 @@ function readStdin(source, signal, idle) {
5972
6155
  });
5973
6156
  }
5974
6157
  //#endregion
5975
- //#region ../agent-cli/dist/input.js
6158
+ //#region ../agent-cli/dist/command-line/input.js
5976
6159
  function isWrapper(schema) {
5977
6160
  return schema instanceof ZodOptional || schema instanceof ZodNullable || schema instanceof ZodDefault || schema instanceof ZodPrefault;
5978
6161
  }
@@ -6074,190 +6257,52 @@ function checkNoBooleanArrayOption(name, options) {
6074
6257
  if (booleans.length > 0) throw new Error(`Command '${name}' declares boolean array option ${keysOf(booleans)}; a switch cannot repeat, use a boolean`);
6075
6258
  }
6076
6259
  //#endregion
6077
- //#region ../agent-cli/dist/tokenize.js
6078
- const NEGATIVE_NUMBER = /^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/;
6079
- function isOptionLike(word) {
6080
- return word !== void 0 && word.length > 1 && word[0] === "-" && !NEGATIVE_NUMBER.test(word);
6260
+ //#region ../agent-cli/dist/command-line/usage.js
6261
+ const INVALID_INPUT = "INVALID_INPUT";
6262
+ const UNKNOWN_OPTION = "UNKNOWN_OPTION";
6263
+ const UNKNOWN_COMMAND = "UNKNOWN_COMMAND";
6264
+ function unknownError({ code, problem, commandLine, corrections, listed }) {
6265
+ return new UsageError({
6266
+ code,
6267
+ message: `${problem} for \`${commandLine}\``,
6268
+ hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
6269
+ });
6081
6270
  }
6082
- function parseArgsOptions(known) {
6083
- return Object.fromEntries(known.map(({ flag, takes }) => [flag.slice(2), { type: takes === "value" ? "string" : "boolean" }]));
6271
+ function unknownOptionError(commandLine, invocation, flag, suggestions) {
6272
+ return unknownError({
6273
+ code: UNKNOWN_OPTION,
6274
+ problem: `unknown option '${flag}'`,
6275
+ commandLine,
6276
+ corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
6277
+ listed: "options"
6278
+ });
6084
6279
  }
6085
- function flagItem(token, { flag, takes }, words) {
6086
- if (takes !== "optional value" || token.inlineValue === true) return {
6087
- kind: "flag",
6088
- flag,
6089
- value: token.value ?? true
6090
- };
6091
- const took = token.index + 1;
6092
- const next = words[took];
6093
- return next === void 0 || isOptionLike(next) ? {
6094
- kind: "flag",
6095
- flag,
6096
- value: true
6097
- } : {
6098
- kind: "flag",
6099
- flag,
6100
- value: next,
6101
- took
6102
- };
6280
+ /** Text opening with `-` was read as an option, where the Command could have read it from stdin. */
6281
+ function stdinTextError(commandLine, invocation, word) {
6282
+ const withStdin = commandLineOf(invocation, {
6283
+ remove: [word],
6284
+ add: ["-"]
6285
+ });
6286
+ return new UsageError({
6287
+ code: UNKNOWN_OPTION,
6288
+ message: `unknown option '${word}' for \`${commandLine}\``,
6289
+ hint: `to pass text opening with \`-\`, pass \`-\` and send the text on stdin through a heredoc: \`${withStdin} <<'EOF'\`, then the text, then \`EOF\``
6290
+ });
6103
6291
  }
6104
- function itemOf(token, words, byFlag) {
6105
- const word = words[token.index] ?? "";
6106
- if (token.kind === "option-terminator") return { kind: "end" };
6107
- if (token.kind === "positional" || NEGATIVE_NUMBER.test(word)) return {
6108
- kind: "operand",
6109
- value: word
6110
- };
6111
- const flag = byFlag.get(token.rawName);
6112
- return flag === void 0 || flag.takes === "nothing" && token.inlineValue === true ? {
6113
- kind: "unknown option",
6114
- word
6115
- } : flagItem(token, flag, words);
6292
+ function unknownCommandError(commandLine, name, suggestions) {
6293
+ return unknownError({
6294
+ code: UNKNOWN_COMMAND,
6295
+ problem: `unknown command '${name}'`,
6296
+ commandLine,
6297
+ corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
6298
+ listed: "commands"
6299
+ });
6116
6300
  }
6117
- /** Splits a Command's words into operands, unknown options and the values of known flags. */
6118
- function tokenize(words, known) {
6119
- const byFlag = new Map(known.map((flag) => [flag.flag, flag]));
6120
- const { tokens } = parseArgs({
6121
- args: [...words],
6122
- options: parseArgsOptions(known),
6123
- strict: false,
6124
- allowPositionals: true,
6125
- tokens: true
6126
- });
6127
- const operands = [];
6128
- const unknownOptions = [];
6129
- const occurrences = /* @__PURE__ */ new Map();
6130
- const done = /* @__PURE__ */ new Set();
6131
- let literal = false;
6132
- let afterUnknownOption = false;
6133
- for (const token of tokens) {
6134
- if (done.has(token.index)) continue;
6135
- done.add(token.index);
6136
- const item = itemOf(token, words, byFlag);
6137
- if (item.kind === "end") literal = true;
6138
- else if (item.kind === "operand") {
6139
- operands.push({
6140
- value: item.value,
6141
- afterUnknownOption: !literal && afterUnknownOption
6142
- });
6143
- afterUnknownOption = false;
6144
- } else if (item.kind === "unknown option") {
6145
- unknownOptions.push(item.word);
6146
- afterUnknownOption = true;
6147
- } else {
6148
- occurrences.set(item.flag, [...occurrences.get(item.flag) ?? [], item.value]);
6149
- if (item.took !== void 0) done.add(item.took);
6150
- }
6151
- }
6152
- return {
6153
- operands,
6154
- unknownOptions,
6155
- values: new Map([...occurrences].map(([flag, all]) => [flag, all[all.length - 1] ?? true])),
6156
- occurrences
6157
- };
6158
- }
6159
- const SHELL_SAFE = /^[\w@%+=:,./-]+$/;
6160
- function quoted$1(token) {
6161
- return SHELL_SAFE.test(token) ? token : `'${token.replaceAll("'", `'\\''`)}'`;
6162
- }
6163
- function pasteable(runAs, tokens, stdinArg) {
6164
- const line = [runAs, ...tokens.map(quoted$1)].join(" ");
6165
- return stdinArg === void 0 ? line : `${line} < <${stdinArg} file>`;
6166
- }
6167
- function split(argv) {
6168
- const end = argv.indexOf("--");
6169
- return end === -1 ? {
6170
- options: [...argv],
6171
- operands: []
6172
- } : {
6173
- options: argv.slice(0, end),
6174
- operands: argv.slice(end)
6175
- };
6176
- }
6177
- function tookValue(takes, next) {
6178
- return takes === "value" || takes === "optional value" && next !== void 0 && !isOptionLike(next);
6179
- }
6180
- function without(flag, takes, tokens) {
6181
- const kept = [];
6182
- for (let index = 0; index < tokens.length; index += 1) {
6183
- const token = tokens[index] ?? "";
6184
- if (token === flag) {
6185
- if (tookValue(takes, tokens[index + 1])) index += 1;
6186
- } else if (!token.startsWith(`${flag}=`)) kept.push(token);
6187
- }
6188
- return kept;
6189
- }
6190
- /**
6191
- * The caller's command line as typed, ready to paste, with each flag in `remove` dropped along with
6192
- * its value, the words in `add` placed before any `--`, and the stdin arg's file redirected in.
6193
- */
6194
- function commandLineOf({ runAs, argv, flags, stdinArg }, { add = [], remove = [] } = {}) {
6195
- const { options, operands } = split(argv);
6196
- const takes = new Map(flags.map((known) => [known.flag, known.takes]));
6197
- return pasteable(runAs, [
6198
- ...remove.reduce((tokens, flag) => without(flag, takes.get(flag) ?? "nothing", tokens), options),
6199
- ...add,
6200
- ...operands
6201
- ], stdinArg);
6202
- }
6203
- function renamed(token, renames) {
6204
- const [flag = token, ...value] = token.split("=");
6205
- const name = renames.get(flag);
6206
- if (name === void 0) return token;
6207
- return value.length === 0 ? name : `${name}=${value.join("=")}`;
6208
- }
6209
- /** The caller's command line, ready to paste, with each flag in `renames` typed as its new name. */
6210
- function commandLineRenaming({ runAs, argv, stdinArg }, renames) {
6211
- const { options, operands } = split(argv);
6212
- return pasteable(runAs, [...options.map((token) => renamed(token, renames)), ...operands], stdinArg);
6213
- }
6214
- //#endregion
6215
- //#region ../agent-cli/dist/usage.js
6216
- const INVALID_INPUT = "INVALID_INPUT";
6217
- const UNKNOWN_OPTION = "UNKNOWN_OPTION";
6218
- const UNKNOWN_COMMAND = "UNKNOWN_COMMAND";
6219
- function unknownError({ code, problem, commandLine, corrections, listed }) {
6220
- return new UsageError({
6221
- code,
6222
- message: `${problem} for \`${commandLine}\``,
6223
- hint: corrections.length === 0 ? `run \`${commandLine} --help\` to see the ${listed}` : `did you mean ${corrections.map((correction) => `\`${correction}\``).join(" or ")}?`
6224
- });
6225
- }
6226
- function unknownOptionError(commandLine, invocation, flag, suggestions) {
6227
- return unknownError({
6228
- code: UNKNOWN_OPTION,
6229
- problem: `unknown option '${flag}'`,
6230
- commandLine,
6231
- corrections: suggestions.map((name) => commandLineRenaming(invocation, /* @__PURE__ */ new Map([[flag, name]]))),
6232
- listed: "options"
6233
- });
6234
- }
6235
- /** Text opening with `-` was read as an option, where the Command could have read it from stdin. */
6236
- function stdinTextError(commandLine, invocation, word) {
6237
- const withStdin = commandLineOf(invocation, {
6238
- remove: [word],
6239
- add: ["-"]
6240
- });
6241
- return new UsageError({
6242
- code: UNKNOWN_OPTION,
6243
- message: `unknown option '${word}' for \`${commandLine}\``,
6244
- hint: `to pass text opening with \`-\`, pass \`-\` and send the text on stdin through a heredoc: \`${withStdin} <<'EOF'\`, then the text, then \`EOF\``
6245
- });
6246
- }
6247
- function unknownCommandError(commandLine, name, suggestions) {
6248
- return unknownError({
6249
- code: UNKNOWN_COMMAND,
6250
- problem: `unknown command '${name}'`,
6251
- commandLine,
6252
- corrections: suggestions.map((suggestion) => `${commandLine} ${suggestion}`),
6253
- listed: "commands"
6254
- });
6255
- }
6256
- function invalidInputError(commandLine, problem) {
6257
- return new UsageError({
6258
- code: INVALID_INPUT,
6259
- message: problem,
6260
- hint: `run \`${commandLine} --help\` to see the expected input`
6301
+ function invalidInputError(commandLine, problem) {
6302
+ return new UsageError({
6303
+ code: INVALID_INPUT,
6304
+ message: problem,
6305
+ hint: `run \`${commandLine} --help\` to see the expected input`
6261
6306
  });
6262
6307
  }
6263
6308
  //#endregion
@@ -6365,16 +6410,19 @@ function cursorWords(cursor) {
6365
6410
  function printedPage(page, invocation) {
6366
6411
  return page.nextCursor === void 0 ? { items: page.items } : {
6367
6412
  items: page.items,
6368
- next: commandLineOf(invocation, {
6369
- remove: [PAGE_FLAGS.cursor.flag],
6370
- add: cursorWords(page.nextCursor)
6371
- })
6413
+ next: lineOf({
6414
+ kind: "rerun",
6415
+ words: changedArgv(invocation, {
6416
+ remove: [PAGE_FLAGS.cursor.flag],
6417
+ add: cursorWords(page.nextCursor)
6418
+ }),
6419
+ stdinArg: invocation.stdinArg
6420
+ }, invocation.runAs)
6372
6421
  };
6373
6422
  }
6374
- /** The line that ends a rendered page, so the caller always knows whether and how to get more. */
6375
- function pageFooter({ items, next }) {
6376
- const more = next === void 0 ? "no more pages" : `next page: \`${next}\``;
6377
- return `${items.length} shown; ${more}\n`;
6423
+ /** The lines that end a rendered page, so the caller always knows whether and how to get more. */
6424
+ function pageFooter({ items, next }, style) {
6425
+ return next === void 0 ? `${items.length} shown; no more pages\n` : `${items.length} shown; run the Next step for the next page\n${style.cyan("run:")} ${next}\n`;
6378
6426
  }
6379
6427
  //#endregion
6380
6428
  //#region ../agent-cli/dist/planned.js
@@ -6383,7 +6431,7 @@ function hasPlan(command) {
6383
6431
  return command.effect === "write" || command.effect === "destructive";
6384
6432
  }
6385
6433
  //#endregion
6386
- //#region ../agent-cli/dist/flags.js
6434
+ //#region ../agent-cli/dist/command-line/flags.js
6387
6435
  const HELP_FLAG = "--help";
6388
6436
  const VERSION_FLAG = "--version";
6389
6437
  const ROUTING_FLAGS = [
@@ -6391,7 +6439,7 @@ const ROUTING_FLAGS = [
6391
6439
  "-h",
6392
6440
  VERSION_FLAG
6393
6441
  ];
6394
- function nameOf(flag) {
6442
+ function nameOf$1(flag) {
6395
6443
  return flag.replace(/^--?/, "");
6396
6444
  }
6397
6445
  function standardFlag(flag, { placeholder, ...rest }) {
@@ -6444,7 +6492,7 @@ const RESERVED = new Map([
6444
6492
  YES_FLAG.flag,
6445
6493
  FORCE_FLAG.flag,
6446
6494
  ...LIST_FLAGS
6447
- ].map((flag) => [nameOf(flag), flag]));
6495
+ ].map((flag) => [nameOf$1(flag), flag]));
6448
6496
  /** The Standard flags of a Command with this Effect and declarations, and of a List Command. */
6449
6497
  function standardFlagsFor(effect, { outputs, force, cost }, list) {
6450
6498
  const forceFlag = force === void 0 ? FORCE_FLAG : {
@@ -6459,7 +6507,7 @@ function standardFlagsFor(effect, { outputs, force, cost }, list) {
6459
6507
  ];
6460
6508
  }
6461
6509
  /** Throws when the Command declares an arg or option under a Standard flag's name. */
6462
- function checkNoStandardFlags(command) {
6510
+ function checkNoStandardFlags(command, label) {
6463
6511
  const lines = [...argFields(command.args).map(({ key }) => ({
6464
6512
  kind: "arg",
6465
6513
  key
@@ -6470,40 +6518,46 @@ function checkNoStandardFlags(command) {
6470
6518
  const flag = RESERVED.get(kebab(key));
6471
6519
  return flag === void 0 ? [] : [` ${kind} '${key}' is the Standard flag ${flag}`];
6472
6520
  });
6473
- if (lines.length > 0) throw new Error([`Command '${command.name}' declares Standard flags the Runtime adds; rename them:`, ...lines].join("\n"));
6521
+ if (lines.length > 0) throw new Error([`Command '${label}' declares Standard flags the Runtime adds; rename them:`, ...lines].join("\n"));
6474
6522
  }
6475
6523
  /** Throws when `force` is declared on a Command with no Plan, or says nothing. */
6476
- function checkForce(command) {
6524
+ function checkForce(command, label) {
6477
6525
  const force = "force" in command ? command.force : void 0;
6478
6526
  if (force === void 0) return;
6479
- if (!hasPlan(command)) throw new Error(`Command '${command.name}' declares force, which only a write or destructive Command may`);
6480
- if (force.trim() === "") throw new Error(`Command '${command.name}' declares an empty force; say what ${FORCE_FLAG.flag} replaces`);
6527
+ if (!hasPlan(command)) throw new Error(`Command '${label}' declares force, which only a write or destructive Command may`);
6528
+ if (force.trim() === "") throw new Error(`Command '${label}' declares an empty force; say what ${FORCE_FLAG.flag} replaces`);
6481
6529
  }
6482
6530
  //#endregion
6483
- //#region ../agent-cli/dist/define.js
6484
- function checkedCommand(command) {
6485
- checkNoStandardFlags(command);
6486
- checkForce(command);
6487
- checkArrayArgLast(command.name, command.args);
6488
- checkStdinFields(command.name, command.args, command.options);
6489
- checkNoBooleanArrayOption(command.name, command.options);
6490
- return command;
6531
+ //#region ../agent-cli/dist/define-command.js
6532
+ /** Throws when the Command's declaration breaks a rule its type cannot express; `label` names it. */
6533
+ function checkCommand(command, label, table) {
6534
+ checkNoStandardFlags(command, label);
6535
+ checkForce(command, label);
6536
+ checkArrayArgLast(label, command.args);
6537
+ checkStdinFields(label, command.args, command.options);
6538
+ checkNoBooleanArrayOption(label, command.options);
6539
+ checkErrors(command, label, table);
6540
+ }
6541
+ function checkErrors(command, label, table) {
6542
+ const unknown = (command.errors ?? []).filter((code) => !Object.hasOwn(table, code));
6543
+ if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
6491
6544
  }
6492
6545
  function defineCommandFor() {
6493
- return checkedCommand;
6546
+ return declared;
6494
6547
  }
6495
- const defineCommand$1 = defineCommandFor();
6496
- function isCommand(node) {
6497
- return typeof node.effect === "string";
6548
+ function declared(command) {
6549
+ return command;
6498
6550
  }
6551
+ const defineCommand$1 = defineCommandFor();
6499
6552
  //#endregion
6500
- //#region ../agent-cli/dist/project.js
6553
+ //#region ../agent-cli/dist/context/project.js
6501
6554
  const PROJECT_INVALID = "PROJECT_INVALID";
6502
- function invalid(path, problem, callerLine, options) {
6555
+ function invalid(path, problem, rerun, options) {
6503
6556
  return new CliError({
6504
6557
  code: PROJECT_INVALID,
6505
6558
  message: `the Project file ${path} is invalid: ${problem}`,
6506
- hint: `fix ${path}, then run \`${callerLine}\` again`
6559
+ hint: `fix ${path}, then run the Next step`,
6560
+ next: [rerun]
6507
6561
  }, options);
6508
6562
  }
6509
6563
  function readText(path, fileError) {
@@ -6517,15 +6571,15 @@ function readText(path, fileError) {
6517
6571
  });
6518
6572
  }
6519
6573
  }
6520
- function parsed(path, text, schema, callerLine) {
6574
+ function parsed(path, text, schema, rerun) {
6521
6575
  let json;
6522
6576
  try {
6523
6577
  json = JSON.parse(text);
6524
6578
  } catch (error) {
6525
- throw invalid(path, messageOf(error), callerLine, { cause: error });
6579
+ throw invalid(path, messageOf(error), rerun, { cause: error });
6526
6580
  }
6527
6581
  const result = schema.safeParse(json);
6528
- if (!result.success) throw invalid(path, prettifyError(result.error), callerLine);
6582
+ if (!result.success) throw invalid(path, prettifyError(result.error), rerun);
6529
6583
  return result.data;
6530
6584
  }
6531
6585
  function projectFrom(projectFile, folder, scope) {
@@ -6533,7 +6587,7 @@ function projectFrom(projectFile, folder, scope) {
6533
6587
  const text = readText(path, scope.fileError);
6534
6588
  if (text !== void 0) return {
6535
6589
  root: folder,
6536
- settings: parsed(path, text, projectFile.schema, scope.callerLine)
6590
+ settings: parsed(path, text, projectFile.schema, scope.rerun)
6537
6591
  };
6538
6592
  const parent = dirname(folder);
6539
6593
  return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
@@ -6543,7 +6597,110 @@ function projectOf(projectFile, cwd, scope) {
6543
6597
  return projectFrom(projectFile, resolve(cwd), scope);
6544
6598
  }
6545
6599
  //#endregion
6546
- //#region ../agent-cli/dist/tools.js
6600
+ //#region ../agent-cli/dist/define.js
6601
+ function isCommand(node) {
6602
+ return typeof node.effect === "string";
6603
+ }
6604
+ //#endregion
6605
+ //#region ../agent-cli/dist/context/declared.js
6606
+ /**
6607
+ * What the CLI declares itself (`own`) and every Command under `commands` declares, each name once,
6608
+ * for a `doctor` row: a Command requires only its own, but `doctor` checks them all.
6609
+ */
6610
+ function declaredIn(own, commands, needsOf) {
6611
+ const walk = (node) => isCommand(node) ? [...needsOf(node) ?? []] : Object.values(node).flatMap(walk);
6612
+ return [...new Map([...own, ...walk(commands)].map((need) => [need.name, need])).values()];
6613
+ }
6614
+ //#endregion
6615
+ //#region ../agent-cli/dist/context/secrets.js
6616
+ const SECRET_MISSING = "SECRET_MISSING";
6617
+ /** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
6618
+ function envFileOf(cliName, env) {
6619
+ const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
6620
+ return join(config, cliName, ".env");
6621
+ }
6622
+ function readDotenv(path, fileError) {
6623
+ let text;
6624
+ try {
6625
+ text = readFileSync(path, "utf8");
6626
+ } catch (error) {
6627
+ if (isMissingFile(error)) return {};
6628
+ throw fileError(error, {
6629
+ path,
6630
+ access: "read"
6631
+ });
6632
+ }
6633
+ return parseEnv(text);
6634
+ }
6635
+ /** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
6636
+ function secretOf({ envFile, env, fileError }, name) {
6637
+ const fromEnv = env[name];
6638
+ if (fromEnv) return {
6639
+ value: fromEnv,
6640
+ origin: { kind: "env" }
6641
+ };
6642
+ const fromFile = readDotenv(envFile, fileError)[name];
6643
+ return fromFile ? {
6644
+ value: fromFile,
6645
+ origin: {
6646
+ kind: "file",
6647
+ path: envFile
6648
+ }
6649
+ } : void 0;
6650
+ }
6651
+ function missingIn(scope, secrets) {
6652
+ return secrets.filter(({ name }) => secretOf(scope, name) === void 0);
6653
+ }
6654
+ function getItFrom({ get }) {
6655
+ return `(get it from ${get})`;
6656
+ }
6657
+ function whereToGet(secrets) {
6658
+ return secrets.map((secret) => `${secret.name} ${getItFrom(secret)}`).join(", ");
6659
+ }
6660
+ /** Fails with `SECRET_MISSING`, naming every missing Secret at once, unless all are set. */
6661
+ function checkSecrets(secrets, scope, rerun) {
6662
+ const missing = missingIn(scope, secrets);
6663
+ if (missing.length === 0) return;
6664
+ const names = missing.map(({ name }) => name).join(", ");
6665
+ throw new CliError({
6666
+ code: SECRET_MISSING,
6667
+ message: `${missing.length === 1 ? "a Secret it needs is" : "Secrets it needs are"} not set: ${names}`,
6668
+ hint: `set ${whereToGet(missing)} in the environment or in ${scope.envFile}, then run the Next step`,
6669
+ next: [rerun]
6670
+ });
6671
+ }
6672
+ function originOf({ origin }) {
6673
+ return origin.kind === "env" ? "the environment" : origin.path;
6674
+ }
6675
+ /** The `doctor` row over every distinct Secret the CLI and its Commands declare; none without any. */
6676
+ function secretRows(secrets, commands) {
6677
+ const declared = declaredIn(secrets, commands, (command) => command.secrets);
6678
+ if (declared.length === 0) return [];
6679
+ return [{
6680
+ name: "secrets",
6681
+ run: ({ env, envFile, fileError }) => {
6682
+ const read = declared.map((declaration) => ({
6683
+ declaration,
6684
+ secret: secretOf({
6685
+ env,
6686
+ envFile,
6687
+ fileError
6688
+ }, declaration.name)
6689
+ }));
6690
+ const missing = read.filter(({ secret }) => secret === void 0);
6691
+ if (missing.length > 0) return {
6692
+ state: "problem",
6693
+ message: `${missing.map(({ declaration }) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
6694
+ };
6695
+ return {
6696
+ state: "ok",
6697
+ message: read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]).join(", ")
6698
+ };
6699
+ }
6700
+ }];
6701
+ }
6702
+ //#endregion
6703
+ //#region ../agent-cli/dist/context/tools.js
6547
6704
  const TOOL_MISSING = "TOOL_MISSING";
6548
6705
  const WINDOWS_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
6549
6706
  function pathOf(env) {
@@ -6573,23 +6730,21 @@ async function findTool(name, env) {
6573
6730
  for (const file of candidates) if (await isExecutable(file)) return file;
6574
6731
  }
6575
6732
  /** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
6576
- async function checkTools(tools, env, callerLine) {
6733
+ async function checkTools(tools, env, rerun) {
6577
6734
  const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
6578
6735
  const missing = tools.filter((_tool, index) => found[index] === void 0);
6579
6736
  if (missing.length === 0) return;
6580
6737
  const names = missing.map(({ name }) => name).join(", ");
6581
- throw new CliError({
6738
+ throw runtimeError({
6582
6739
  code: TOOL_MISSING,
6583
6740
  message: `${missing.length === 1 ? "a tool it runs is" : "tools it runs are"} not on PATH: ${names}`,
6584
- hint: `install ${missing.map(({ name, install }) => `${name} (\`${install}\`)`).join(", ")}, then run \`${callerLine}\` again`
6741
+ hint: "run the Next steps in order: each install, then the command again",
6742
+ next: [...missing.map(({ install }) => wholeLine(install)), rerun]
6585
6743
  });
6586
6744
  }
6587
- function toolsIn(node) {
6588
- return isCommand(node) ? [...node.tools ?? []] : Object.values(node).flatMap(toolsIn);
6589
- }
6590
6745
  /** One `doctor` row per distinct tool the CLI and its Commands declare, with its install command as the fix. */
6591
6746
  function toolRows(tools, commands) {
6592
- return [...new Map([...tools, ...toolsIn(commands)].map((tool) => [tool.name, tool])).values()].map(({ name, install }) => ({
6747
+ return declaredIn(tools, commands, (command) => command.tools).map(({ name, install }) => ({
6593
6748
  name,
6594
6749
  run: async (ctx) => {
6595
6750
  const file = await findTool(name, ctx.env);
@@ -6606,72 +6761,18 @@ function toolRows(tools, commands) {
6606
6761
  }
6607
6762
  //#endregion
6608
6763
  //#region ../agent-cli/dist/version.js
6609
- /** The first line of `--version`: the version, and the commit and time of a stamped build. */
6610
- function versionLine({ version, build }) {
6611
- return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
6612
- }
6613
- //#endregion
6614
- //#region ../agent-cli/dist/project-root.js
6615
- const MANAGERS = [
6616
- "pnpm",
6617
- "npm",
6618
- "yarn",
6619
- "bun"
6620
- ];
6621
- const LOCKFILES = [
6622
- ["pnpm-lock.yaml", "pnpm"],
6623
- ["package-lock.json", "npm"],
6624
- ["yarn.lock", "yarn"],
6625
- ["bun.lock", "bun"],
6626
- ["bun.lockb", "bun"]
6627
- ];
6628
- const manifestSchema$1 = object({
6629
- packageManager: string().optional(),
6630
- dependencies: record(string(), unknown()).optional(),
6631
- devDependencies: record(string(), unknown()).optional()
6632
- });
6633
- /** `dir` and every folder above it, nearest first. */
6634
- function ancestorsOf(dir) {
6635
- const parent = dirname(dir);
6636
- return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
6637
- }
6638
- async function exists(path) {
6639
- return await orMissing(stat(path)) !== void 0;
6640
- }
6641
- /** Whether `path` lies strictly inside `dir`. */
6642
- function isInside(dir, path) {
6643
- const inner = relative(dir, path);
6644
- return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
6645
- }
6646
- async function manifestIn(dir) {
6647
- let text;
6648
- try {
6649
- text = await readFile(join(dir, "package.json"), "utf8");
6650
- } catch (error) {
6651
- if (isMissingFile(error)) return void 0;
6652
- throw error;
6653
- }
6654
- let manifest;
6655
- try {
6656
- manifest = JSON.parse(text);
6657
- } catch {
6658
- return;
6659
- }
6660
- return manifestSchema$1.safeParse(manifest).data;
6661
- }
6662
- async function declaredManager(dir) {
6663
- const name = (await manifestIn(dir))?.packageManager?.split("@")[0];
6664
- return MANAGERS.find((manager) => manager === name);
6665
- }
6666
- async function lockfileRootOf(cwd) {
6667
- for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await exists(join(dir, file))) return {
6668
- dir,
6669
- manager: await declaredManager(dir) ?? kind
6764
+ /** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
6765
+ function buildStamp() {
6766
+ return {
6767
+ "sha": "e9645e3",
6768
+ "dirty": false,
6769
+ "builtAt": "2026-10-28T08:17:12.000Z"
6670
6770
  };
6671
6771
  }
6672
- /** The package manager's project root: the folder of the nearest lockfile, or `cwd` without one. */
6673
- async function projectRootOf(cwd) {
6674
- return (await lockfileRootOf(cwd))?.dir ?? cwd;
6772
+ /** The first line of `--version`: the version, and the commit and time of a stamped build. */
6773
+ function versionLine({ version }) {
6774
+ const build = buildStamp();
6775
+ return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
6675
6776
  }
6676
6777
  //#endregion
6677
6778
  //#region ../agent-cli/dist/tail.js
@@ -6680,7 +6781,7 @@ function lastLines(output, count) {
6680
6781
  return output.trim().split("\n").slice(-count).join("\n");
6681
6782
  }
6682
6783
  //#endregion
6683
- //#region ../agent-cli/dist/spawn.js
6784
+ //#region ../agent-cli/dist/context/spawn.js
6684
6785
  const TOOL_FAILED = "TOOL_FAILED";
6685
6786
  const STDERR_TAIL_LINES$1 = 20;
6686
6787
  const SIGNAL_EXIT_BASE = 128;
@@ -6905,15 +7006,16 @@ function failedError(command, { exitCode, stderr }) {
6905
7006
  hint: `the output of ${command} above says why it failed`
6906
7007
  });
6907
7008
  }
6908
- function missingError(command, callerLine, cause) {
7009
+ function missingError(command, rerun, cause) {
6909
7010
  return new CliError({
6910
7011
  code: TOOL_MISSING,
6911
7012
  message: `${command} could not be started: it is not on PATH`,
6912
- hint: `install ${command}, then run \`${callerLine}\` again`
7013
+ hint: `install ${command}, then run the Next step`,
7014
+ next: [rerun]
6913
7015
  }, { cause });
6914
7016
  }
6915
- function startError(command, error, { fileError, callerLine }) {
6916
- if (isMissingFile(error)) return missingError(command, callerLine, error);
7017
+ function startError(command, error, { fileError, rerun }) {
7018
+ if (isMissingFile(error)) return missingError(command, rerun, error);
6917
7019
  return fileError(error, {
6918
7020
  path: command,
6919
7021
  access: "read"
@@ -7050,7 +7152,7 @@ const NO_LATEST = "NO_LATEST";
7050
7152
  function npmContext(cli, registry, ctx, node, cwd) {
7051
7153
  return {
7052
7154
  cliName: cli.name,
7053
- callerLine: ctx.commandLine(),
7155
+ rerun: ctx.rerun(),
7054
7156
  registry,
7055
7157
  cwd,
7056
7158
  env: ctx.env,
@@ -7059,6 +7161,7 @@ function npmContext(cli, registry, ctx, node, cwd) {
7059
7161
  };
7060
7162
  }
7061
7163
  const STDERR_TAIL_LINES = 10;
7164
+ const RUN_AGAIN = "run it again";
7062
7165
  const RM_RETRIES = 3;
7063
7166
  const NETWORK_CODES = /* @__PURE__ */ new Set([
7064
7167
  "ENOTFOUND",
@@ -7122,27 +7225,31 @@ function failureOf(call, answer, ctx) {
7122
7225
  const { label: registry, package: name, loginHint, unreachableHint } = ctx.registry;
7123
7226
  const code = NPM_CODE.exec(answer.stderr)?.[1];
7124
7227
  const output = tail$1(answer.stderr);
7125
- const rerun = `run \`${ctx.callerLine}\` again`;
7228
+ const next = [ctx.rerun];
7126
7229
  if (code !== void 0 && NETWORK_CODES.has(code)) return new CliError({
7127
7230
  code: REGISTRY_UNREACHABLE,
7128
7231
  message: `npm could not reach ${registry} (${code})${output}`,
7129
- hint: `${unreachableHint}, then ${rerun}`
7232
+ hint: `${unreachableHint}, then ${RUN_AGAIN}`,
7233
+ next
7130
7234
  });
7131
7235
  if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
7132
7236
  code: REGISTRY_AUTH,
7133
7237
  message: `${registry} refused npm's credentials (${code})${output}`,
7134
- hint: `${loginHint}, then ${rerun}`
7238
+ hint: `${loginHint}, then ${RUN_AGAIN}`,
7239
+ next
7135
7240
  });
7136
7241
  if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
7137
- if (call === "install") return new CliError({
7242
+ if (call === "install") return runtimeError({
7138
7243
  code: INSTALL_FAILED,
7139
7244
  message: `npm could not install ${name}${code === void 0 ? "" : ` (${code})`}${output}`,
7140
- hint: `fix what npm reports, then ${rerun}, or install it yourself: \`${installCommandLine(ctx.registry)}\``
7245
+ hint: `fix what npm reports, then ${RUN_AGAIN} with the first Next step, or install it yourself with the second`,
7246
+ next: [...next, wholeLine(installCommandLine(ctx.registry))]
7141
7247
  });
7142
7248
  return new CliError({
7143
7249
  code: NPM_FAILED,
7144
7250
  message: `npm ${call} failed with exit code ${answer.exitCode}${output}`,
7145
- hint: `fix what npm reports, then ${rerun}`
7251
+ hint: `fix what npm reports, then ${RUN_AGAIN}`,
7252
+ next
7146
7253
  });
7147
7254
  }
7148
7255
  function noLatest(ctx, message) {
@@ -7218,10 +7325,73 @@ async function latestVersion(ctx) {
7218
7325
  return version;
7219
7326
  }
7220
7327
  //#endregion
7221
- //#region ../agent-cli/dist/built-ins/install/run-as.js
7222
- /** The Run-as: the bare name, or `npx -q --no-install <bin>` for a Dependency install (ADR 0011). */
7223
- function runAsOf({ name, install }) {
7224
- return install?.mode === "dependency" ? `npx -q --no-install ${name}` : name;
7328
+ //#region ../agent-cli/dist/built-ins/project-root.js
7329
+ const MANAGERS = [
7330
+ "pnpm",
7331
+ "npm",
7332
+ "yarn",
7333
+ "bun"
7334
+ ];
7335
+ const LOCKFILES = [
7336
+ ["pnpm-lock.yaml", "pnpm"],
7337
+ ["package-lock.json", "npm"],
7338
+ ["yarn.lock", "yarn"],
7339
+ ["bun.lock", "bun"],
7340
+ ["bun.lockb", "bun"]
7341
+ ];
7342
+ const manifestSchema$1 = object({
7343
+ packageManager: string().optional(),
7344
+ dependencies: record(string(), unknown()).optional(),
7345
+ devDependencies: record(string(), unknown()).optional()
7346
+ });
7347
+ /** `dir` and every folder above it, nearest first. */
7348
+ function ancestorsOf(dir) {
7349
+ const parent = dirname(dir);
7350
+ return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
7351
+ }
7352
+ async function exists(path) {
7353
+ return await orMissing(stat(path)) !== void 0;
7354
+ }
7355
+ /** Whether `path` lies strictly inside `dir`. */
7356
+ function isInside(dir, path) {
7357
+ const inner = relative(dir, path);
7358
+ return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
7359
+ }
7360
+ async function manifestIn(dir) {
7361
+ let text;
7362
+ try {
7363
+ text = await readFile(join(dir, "package.json"), "utf8");
7364
+ } catch (error) {
7365
+ if (isMissingFile(error)) return void 0;
7366
+ throw error;
7367
+ }
7368
+ let manifest;
7369
+ try {
7370
+ manifest = JSON.parse(text);
7371
+ } catch {
7372
+ return;
7373
+ }
7374
+ return manifestSchema$1.safeParse(manifest).data;
7375
+ }
7376
+ async function declaredManager(dir) {
7377
+ const name = (await manifestIn(dir))?.packageManager?.split("@")[0];
7378
+ return MANAGERS.find((manager) => manager === name);
7379
+ }
7380
+ async function lockfileRootOf(cwd) {
7381
+ for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await exists(join(dir, file))) return {
7382
+ dir,
7383
+ manager: await declaredManager(dir) ?? kind
7384
+ };
7385
+ }
7386
+ /** The package manager's project root: the folder of the nearest lockfile, or `cwd` without one. */
7387
+ async function projectRootOf(cwd) {
7388
+ return (await lockfileRootOf(cwd))?.dir ?? cwd;
7389
+ }
7390
+ //#endregion
7391
+ //#region ../agent-cli/dist/built-ins/install/run-as.js
7392
+ /** The Run-as: the bare name, or `npx -q --no-install <bin>` for a Dependency install (ADR 0011). */
7393
+ function runAsOf({ name, install }) {
7394
+ return install?.mode === "dependency" ? `npx -q --no-install ${name}` : name;
7225
7395
  }
7226
7396
  //#endregion
7227
7397
  //#region ../agent-cli/dist/built-ins/install/dependency.js
@@ -7445,13 +7615,18 @@ async function skillCopies(cliName, version, { cwd, env }) {
7445
7615
  }
7446
7616
  return copies;
7447
7617
  }
7448
- function fixFor(cliName, copies, source) {
7449
- const has = (wanted) => copies.some(({ state }) => state === wanted);
7618
+ const has = (copies, wanted) => copies.some(({ state }) => state === wanted);
7619
+ function fixFor(cliName, copies, from) {
7450
7620
  const update = `npx -y skills update ${cliName}`;
7451
- const add = source === void 0 ? void 0 : `npx -y skills add ${source} --skill ${cliName} -g`;
7621
+ const add = from !== void 0 && "source" in from ? `npx -y skills add ${from.source} --skill ${cliName} -g` : void 0;
7452
7622
  if (copies.length === 0) return add;
7453
- if (has("unversioned") && add !== void 0) return has("behind") ? `${update} && ${add}` : add;
7454
- return has("behind") || has("unversioned") ? update : void 0;
7623
+ if (has(copies, "unversioned") && add !== void 0) return has(copies, "behind") ? `${update} && ${add}` : add;
7624
+ if (from !== void 0 && "page" in from) return has(copies, "behind") ? update : void 0;
7625
+ return has(copies, "behind") || has(copies, "unversioned") ? update : void 0;
7626
+ }
7627
+ function pageNote(copies, from) {
7628
+ if (from === void 0 || !("page" in from)) return;
7629
+ return copies.length === 0 || has(copies, "unversioned") ? `copy your install line from ${from.page}` : void 0;
7455
7630
  }
7456
7631
  const SKILL_STATE_NOTES = {
7457
7632
  current: "the same as this build",
@@ -7463,12 +7638,13 @@ const SKILL_STATE_NOTES = {
7463
7638
  function describeCopy({ path, version, state }) {
7464
7639
  return `${path}${version === void 0 ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
7465
7640
  }
7466
- /** A skill that `npx skills add` installs from `source`, the Skill Hub; with no `source`, no line adds it. */
7467
- function hubSkill(cli, source) {
7468
- const findingAt = async (version, ctx) => {
7469
- const copies = await skillCopies(cli.name, version, ctx);
7470
- const fix = fixFor(cli.name, copies, source);
7471
- const message = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
7641
+ /** A skill on a Skill Hub, which `from` says how to install; with neither, no line adds it. */
7642
+ function hubSkill(cli, from) {
7643
+ const findingOf = (copies) => {
7644
+ const fix = fixFor(cli.name, copies, from);
7645
+ const note = pageNote(copies, from);
7646
+ const found = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
7647
+ const message = note === void 0 ? found : `${found}; ${note}`;
7472
7648
  return fix === void 0 ? {
7473
7649
  state: "ok",
7474
7650
  message
@@ -7484,10 +7660,16 @@ function hubSkill(cli, source) {
7484
7660
  rows: [{
7485
7661
  name: "skill",
7486
7662
  advisory: true,
7487
- run: (ctx) => findingAt(cli.version, ctx)
7663
+ run: async (ctx) => findingOf(await skillCopies(cli.name, cli.version, ctx))
7488
7664
  }],
7489
7665
  notice: async () => {},
7490
- findingAt
7666
+ nextAt: async (version, ctx) => {
7667
+ const copies = await skillCopies(cli.name, version, ctx);
7668
+ const note = pageNote(copies, from);
7669
+ if (note !== void 0) ctx.notice(note);
7670
+ const fix = fixFor(cli.name, copies, from);
7671
+ return fix === void 0 ? [] : [fix];
7672
+ }
7491
7673
  };
7492
7674
  }
7493
7675
  //#endregion
@@ -7498,10 +7680,10 @@ function markersOf(name) {
7498
7680
  end: `<!-- END:${name} -->`
7499
7681
  };
7500
7682
  }
7501
- /** `text` with the CLI's block between its markers, appended when they are missing; nothing else changes. */
7502
- function withBlock({ name, text: block }, text) {
7683
+ /** `text` with the CLI's Agents section between its markers, appended when they are missing; nothing else changes. */
7684
+ function withSection({ name, text: section }, text) {
7503
7685
  const { begin, end } = markersOf(name);
7504
- const whole = `${begin}\n${block}\n${end}`;
7686
+ const whole = `${begin}\n${section}\n${end}`;
7505
7687
  const start = text.indexOf(begin);
7506
7688
  const stop = start === -1 ? -1 : text.indexOf(end, start);
7507
7689
  if (stop !== -1) return `${text.slice(0, start)}${whole}${text.slice(stop + end.length)}`;
@@ -7516,7 +7698,7 @@ async function agentsMdAt(agents, root) {
7516
7698
  agents,
7517
7699
  change: "create"
7518
7700
  };
7519
- if (withBlock(agents, existing) === existing) return {
7701
+ if (withSection(agents, existing) === existing) return {
7520
7702
  path,
7521
7703
  agents,
7522
7704
  change: null
@@ -7544,15 +7726,15 @@ function agentsRow(agents, install) {
7544
7726
  const fix = `${install.runAs} skill install`;
7545
7727
  if (change === "rewrite") return {
7546
7728
  state: "problem",
7547
- message: `${path} holds a ${name} block other than the one ${fix} writes`,
7729
+ message: `${path} holds a ${name} Agents section other than the one ${fix} writes`,
7548
7730
  fix
7549
7731
  };
7550
7732
  if (change !== null) return {
7551
7733
  state: "problem",
7552
- message: `${path} holds no ${name} block, so sessions in this repo do not know ${name}`,
7734
+ message: `${path} holds no ${name} Agents section, so sessions in this repo do not know ${name}`,
7553
7735
  fix
7554
7736
  };
7555
- const holds = `${path} holds the ${name} block`;
7737
+ const holds = `${path} holds the ${name} Agents section`;
7556
7738
  const [edit] = await handEditsAt(root);
7557
7739
  return edit === void 0 ? {
7558
7740
  state: "ok",
@@ -7565,7 +7747,7 @@ function agentsRow(agents, install) {
7565
7747
  };
7566
7748
  }
7567
7749
  //#endregion
7568
- //#region ../agent-cli/dist/agent.js
7750
+ //#region ../agent-cli/dist/context/agent.js
7569
7751
  const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
7570
7752
  const isSet = (name) => (env) => (env[name] ?? "") !== "";
7571
7753
  const equals = (name, value) => (env) => env[name] === value;
@@ -7599,16 +7781,22 @@ function isAgent(env) {
7599
7781
  return MARKERS.some((marker) => marker(env));
7600
7782
  }
7601
7783
  //#endregion
7602
- //#region ../agent-cli/dist/errno.js
7784
+ //#region ../agent-cli/dist/context/errno.js
7603
7785
  const PERMISSION_DENIED = "PERMISSION_DENIED";
7604
7786
  const DISK_FULL = "DISK_FULL";
7605
7787
  const NOT_FOUND = "NOT_FOUND";
7606
7788
  const ALREADY_EXISTS = "ALREADY_EXISTS";
7607
7789
  const NOT_A_FILE = "NOT_A_FILE";
7608
- function permissionHint({ path, access, callerLine, env, platform }) {
7609
- if (platform === "win32") return `a program may hold ${path} open: close it, then run \`${callerLine}\` again`;
7610
- if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
7611
- 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`}`;
7790
+ function permissionAdvice({ path, access, rerun, env, platform }) {
7791
+ if (platform === "win32") return {
7792
+ hint: `a program may hold ${path} open: close it, then run the Next step`,
7793
+ next: [rerun]
7794
+ };
7795
+ if (!isAgent(env)) return { hint: `check the permissions of ${path} and its folder` };
7796
+ return {
7797
+ hint: `your sandbox may block it: ask your user to type ! then the Next step, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`,
7798
+ next: [rerun]
7799
+ };
7612
7800
  }
7613
7801
  function fileErrorOf(scope) {
7614
7802
  return (error, at) => classifyErrno(error, {
@@ -7625,13 +7813,14 @@ function classifyErrno(error, context) {
7625
7813
  case "EBUSY": return new CliError({
7626
7814
  code: PERMISSION_DENIED,
7627
7815
  message: `permission denied: ${path}`,
7628
- hint: permissionHint(context)
7816
+ ...permissionAdvice(context)
7629
7817
  }, { cause: error });
7630
7818
  case "ENOSPC":
7631
7819
  case "EDQUOT": return new CliError({
7632
7820
  code: DISK_FULL,
7633
7821
  message: `no space left on the disk holding ${path}`,
7634
- hint: `free space on that disk, then run \`${context.callerLine}\` again`
7822
+ hint: "free space on that disk, then run the Next step",
7823
+ next: [context.rerun]
7635
7824
  }, { cause: error });
7636
7825
  case "ENOENT":
7637
7826
  case "ENOTDIR":
@@ -7654,7 +7843,7 @@ function classifyErrno(error, context) {
7654
7843
  }
7655
7844
  }
7656
7845
  //#endregion
7657
- //#region ../agent-cli/dist/files.js
7846
+ //#region ../agent-cli/dist/context/files.js
7658
7847
  const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
7659
7848
  /** Where the Runtime writes `path` before moving it into place; a dead writer's is swept. */
7660
7849
  function tempPathOf(path) {
@@ -7827,10 +8016,10 @@ function classifiedFiles(files, { cwd, fileError }) {
7827
8016
  };
7828
8017
  }
7829
8018
  //#endregion
7830
- //#region ../agent-cli/dist/outputs.js
8019
+ //#region ../agent-cli/dist/context/outputs.js
7831
8020
  const OUTPUT_EXISTS = "OUTPUT_EXISTS";
7832
8021
  /** Refuses a run that would overwrite existing outputs unless the caller passed `--force`. */
7833
- async function checkOutputs({ invocation, commandLine, force, files }, outputs) {
8022
+ async function checkOutputs({ rerun, commandLine, force, files }, outputs) {
7834
8023
  if (outputs === void 0 || force) return;
7835
8024
  const found = await Promise.all(outputs.map((path) => files.exists(path)));
7836
8025
  const existing = outputs.filter((_path, index) => found[index]);
@@ -7839,13 +8028,14 @@ async function checkOutputs({ invocation, commandLine, force, files }, outputs)
7839
8028
  throw new CliError({
7840
8029
  code: OUTPUT_EXISTS,
7841
8030
  message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
7842
- hint: `to replace ${them}, run \`${commandLineOf(invocation, { add: [FORCE_FLAG.flag] })}\``
8031
+ hint: `to replace ${them}, run the Next step`,
8032
+ next: [rerun({ add: [FORCE_FLAG.flag] })]
7843
8033
  });
7844
8034
  }
7845
8035
  /** Guards the writes to declared outputs of `files`, whose paths resolve against `cwd`. */
7846
8036
  function outputsGuard(files, cwd) {
7847
8037
  const unwritten = /* @__PURE__ */ new Set();
7848
- let forceLine = "";
8038
+ let forceStep = [];
7849
8039
  const exclusiveWrite = async (path, data, options) => {
7850
8040
  const target = resolve(cwd, path);
7851
8041
  unwritten.delete(target);
@@ -7860,7 +8050,8 @@ function outputsGuard(files, cwd) {
7860
8050
  throw new CliError({
7861
8051
  code: ALREADY_EXISTS,
7862
8052
  message: error.message,
7863
- hint: `to replace it, run \`${forceLine}\``
8053
+ hint: "to replace it, run the Next step",
8054
+ next: [forceStep]
7864
8055
  }, { cause: error });
7865
8056
  }
7866
8057
  };
@@ -7869,8 +8060,8 @@ function outputsGuard(files, cwd) {
7869
8060
  ...files,
7870
8061
  write: (path, data, options) => unwritten.has(resolve(cwd, path)) ? exclusiveWrite(path, data, options) : files.write(path, data, options)
7871
8062
  },
7872
- guard: (outputs, line) => {
7873
- forceLine = line;
8063
+ guard: (outputs, step) => {
8064
+ forceStep = step;
7874
8065
  for (const path of outputs) unwritten.add(resolve(cwd, path));
7875
8066
  }
7876
8067
  };
@@ -7953,10 +8144,10 @@ async function planOf(cli, folder, agents, cwd, packageRoot) {
7953
8144
  handEdits: agents === void 0 ? [] : await handEditsAt(root)
7954
8145
  };
7955
8146
  }
7956
- const BLOCK_VERBS = {
8147
+ const SECTION_VERBS = {
7957
8148
  create: "create",
7958
- add: "add the block to",
7959
- rewrite: "rewrite the block in"
8149
+ add: "add the Agents section to",
8150
+ rewrite: "rewrite the Agents section in"
7960
8151
  };
7961
8152
  function renderPlan(cli, plan) {
7962
8153
  const files = [
@@ -7968,7 +8159,7 @@ function renderPlan(cli, plan) {
7968
8159
  return [
7969
8160
  files.length === 0 ? `the ${cli.name} skill at ${plan.copy} already matches the package` : `would copy the ${cli.name} skill to ${plan.copy}:`,
7970
8161
  ...files,
7971
- ...agentsMd === null ? [] : [agentsMd.change === null ? `${agentsMd.path} already holds the ${cli.name} block` : `would ${BLOCK_VERBS[agentsMd.change]} ${agentsMd.path}:\n${agentsMd.agents.text}`],
8162
+ ...agentsMd === null ? [] : [agentsMd.change === null ? `${agentsMd.path} already holds the ${cli.name} Agents section` : `would ${SECTION_VERBS[agentsMd.change]} ${agentsMd.path}:\n${agentsMd.agents.text}`],
7972
8163
  ...plan.handEdits.map((edit) => `by hand: ${edit}`)
7973
8164
  ].join("\n");
7974
8165
  }
@@ -7976,7 +8167,7 @@ function renderInstalled(cli, data) {
7976
8167
  const { written, removed, agentsMd } = data;
7977
8168
  return [
7978
8169
  written.length + removed.length === 0 ? `the ${cli.name} skill at ${data.copy} already matches the package` : `copied the ${cli.name} skill to ${data.copy}: wrote ${written.length} files, removed ${removed.length}`,
7979
- ...agentsMd === null ? [] : [agentsMd.changed ? `wrote the ${cli.name} block in ${agentsMd.path}` : `${agentsMd.path} already holds the ${cli.name} block`],
8170
+ ...agentsMd === null ? [] : [agentsMd.changed ? `wrote the ${cli.name} Agents section in ${agentsMd.path}` : `${agentsMd.path} already holds the ${cli.name} Agents section`],
7980
8171
  ...data.handEdits.map((edit) => `by hand: ${edit}`)
7981
8172
  ].join("\n");
7982
8173
  }
@@ -7984,21 +8175,20 @@ function editedRemovals(plan, ctx) {
7984
8175
  const them = plan.remove.length === 1 ? "it" : "them";
7985
8176
  return new CliError({
7986
8177
  code: OUTPUT_EXISTS,
7987
- message: `\`${ctx.commandLine()}\` would remove ${plan.remove.map((file) => join(plan.copy, file)).join(", ")}`,
7988
- hint: `to remove ${them}, run \`${ctx.commandLine({ add: [FORCE_FLAG.flag] })}\``
8178
+ message: `skill install would remove ${plan.remove.map((file) => join(plan.copy, file)).join(", ")}`,
8179
+ hint: `to remove ${them}, run the Next step`,
8180
+ next: [ctx.rerun({ add: [FORCE_FLAG.flag] })]
7989
8181
  });
7990
8182
  }
7991
8183
  /** The built-in `skill install`, for a CLI whose skill ships in its package. */
7992
- function skillInstallCommand(cli, folder, install, agents) {
7993
- const { runAs } = install;
8184
+ function skillInstallCommand(cli, folder, agents) {
7994
8185
  return defineCommand$1({
7995
- name: "install",
7996
- description: agents === void 0 ? "copy this CLI's skill into the project" : "copy this CLI's skill into the project and write its AGENTS.md block",
8186
+ description: agents === void 0 ? "copy this CLI's skill into the project" : "copy this CLI's skill into the project and write its Agents section",
7997
8187
  examples: [{
7998
- command: `${runAs} skill install`,
8188
+ command: `${cli.name} skill install`,
7999
8189
  note: "install the skill this version ships"
8000
8190
  }, {
8001
- command: `${runAs} skill install --dry-run`,
8191
+ command: `${cli.name} skill install --dry-run`,
8002
8192
  note: "show what it would change"
8003
8193
  }],
8004
8194
  effect: "write",
@@ -8013,7 +8203,7 @@ function skillInstallCommand(cli, folder, install, agents) {
8013
8203
  const { agentsMd } = plan;
8014
8204
  if (agentsMd !== null && agentsMd.change !== null) {
8015
8205
  const existing = await orMissing(readFile(agentsMd.path, "utf8")) ?? "";
8016
- await ctx.files.write(agentsMd.path, withBlock(agentsMd.agents, existing));
8206
+ await ctx.files.write(agentsMd.path, withSection(agentsMd.agents, existing));
8017
8207
  }
8018
8208
  return {
8019
8209
  copy: plan.copy,
@@ -8104,45 +8294,53 @@ function packagedSkill(cli, { packaged: folder, agents: text }, install) {
8104
8294
  };
8105
8295
  return {
8106
8296
  kind: "packaged",
8107
- builtIns: { skill: { install: skillInstallCommand(cli, folder, install, agents) } },
8297
+ builtIns: { skill: { install: skillInstallCommand(cli, folder, agents) } },
8108
8298
  rows: [{
8109
8299
  name: "skill",
8110
8300
  advisory: true,
8111
8301
  run: (ctx) => findingOf(cli, folder, install, ctx)
8112
8302
  }, ...agents === void 0 ? [] : [agentsRow(agents, install)]],
8113
8303
  notice: (launch, ctx) => notice(cli, folder, install, launch, ctx),
8114
- findingAt: (_version, ctx) => findingOf(cli, folder, install, ctx)
8304
+ nextAt: async (_version, ctx) => {
8305
+ const { fix } = await findingOf(cli, folder, install, ctx);
8306
+ return fix === void 0 ? [] : [fix];
8307
+ }
8115
8308
  };
8116
8309
  }
8117
8310
  //#endregion
8118
8311
  //#region ../agent-cli/dist/built-ins/skill/skill.js
8119
8312
  /** A Packaged skill, or else a Skill Hub's, also for a CLI that declares no `skill`. */
8120
- function skillOf(cli, install) {
8313
+ function skillOf$1(cli, install) {
8121
8314
  const { skill } = cli;
8122
- return skill !== void 0 && "packaged" in skill ? packagedSkill(cli, skill, install) : hubSkill(cli, skill?.source);
8315
+ return skill !== void 0 && "packaged" in skill ? packagedSkill(cli, skill, install) : hubSkill(cli, skill);
8123
8316
  }
8124
8317
  //#endregion
8125
- //#region ../agent-cli/dist/interrupts.js
8318
+ //#region ../agent-cli/dist/context/interrupts.js
8126
8319
  const INTERRUPTED = "INTERRUPTED";
8127
8320
  const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
8128
8321
  /** How long an interrupted Command may take to settle before the run ends without it. */
8129
8322
  const GRACE_MS = 3e3;
8130
8323
  const GRACE_OVER = Symbol("grace over");
8131
- function interruptedError(callerLine) {
8324
+ function interruptedError(rerun) {
8132
8325
  return new CliError({
8133
8326
  code: INTERRUPTED,
8134
8327
  message: "interrupted before it finished",
8135
- hint: `to finish it, run \`${callerLine}\` again, unless it was stopped on purpose; if a tool timeout stopped it, give it longer`
8328
+ hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
8329
+ next: [rerun]
8136
8330
  });
8137
8331
  }
8138
- function waitHint(callerLine, bound) {
8139
- return bound === "timeout" ? `the call may have been billed: ask your user before running \`${callerLine}\` again` : `run \`${callerLine}\` again later, when the service it waits on may answer faster`;
8332
+ const BOUND_WORDS = {
8333
+ deadline: "deadline",
8334
+ ignoreStop: "timeout"
8335
+ };
8336
+ function waitHint(bound) {
8337
+ return bound === "ignoreStop" ? "the call may have been billed: ask your user before running the Next step" : "run the Next step later, when the service it waits on may answer faster";
8140
8338
  }
8141
- function deadlineError(commandLine, ms, hint, bound = "deadline") {
8339
+ function deadlineError(commandLine, ms, advice, bound = "deadline") {
8142
8340
  return new CliError({
8143
8341
  code: DEADLINE_EXCEEDED,
8144
- message: `${commandLine} ran past its ${ms} ms ${bound}`,
8145
- hint
8342
+ message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
8343
+ ...advice
8146
8344
  });
8147
8345
  }
8148
8346
  /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
@@ -8150,24 +8348,24 @@ var RunControl = class {
8150
8348
  signal;
8151
8349
  #controller = new AbortController();
8152
8350
  #commandLine;
8153
- #callerLine;
8351
+ #rerun;
8154
8352
  #outer;
8155
- #onOuterAbort = () => this.#controller.abort(interruptedError(this.#callerLine));
8353
+ #onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
8156
8354
  /** Each running must-finish step, tracked until it settles, with its name. */
8157
8355
  #steps = /* @__PURE__ */ new Map();
8158
8356
  #progress;
8159
8357
  #clock;
8160
8358
  #disposed = new AbortController();
8161
- /** The reasons this run's deadlines and timeouts aborted with, to tell them in a Command's error. */
8359
+ /** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
8162
8360
  #abortReasons = /* @__PURE__ */ new WeakSet();
8163
8361
  #graceCancel = new AbortController();
8164
8362
  #endGrace = () => {};
8165
8363
  #graceOver = new Promise((resolve) => {
8166
8364
  this.#endGrace = resolve;
8167
8365
  });
8168
- constructor({ commandLine, callerLine, outer, clock, progress, runDeadline }) {
8366
+ constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
8169
8367
  this.#commandLine = commandLine;
8170
- this.#callerLine = callerLine;
8368
+ this.#rerun = rerun;
8171
8369
  this.#outer = outer;
8172
8370
  this.#clock = clock;
8173
8371
  this.#progress = progress;
@@ -8176,20 +8374,21 @@ var RunControl = class {
8176
8374
  if (outer?.aborted === true) this.#onOuterAbort();
8177
8375
  else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
8178
8376
  if (runDeadline !== void 0) {
8179
- const { ms, hint } = runDeadline;
8180
- after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, hint())));
8377
+ const { ms, advice } = runDeadline;
8378
+ after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
8181
8379
  }
8182
8380
  }
8183
- deadline(ms) {
8184
- return AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
8185
- }
8186
- timeout(ms) {
8187
- return this.#abortsAfter(ms, "timeout");
8381
+ deadline(ms, options = {}) {
8382
+ return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
8188
8383
  }
8189
8384
  #abortsAfter(ms, bound) {
8190
8385
  const controller = new AbortController();
8191
8386
  after(this.#clock, ms, this.#disposed.signal, () => {
8192
- const reason = deadlineError(this.#commandLine, ms, waitHint(this.#callerLine, bound), bound);
8387
+ const advice = {
8388
+ hint: waitHint(bound),
8389
+ next: [this.#rerun]
8390
+ };
8391
+ const reason = deadlineError(this.#commandLine, ms, advice, bound);
8193
8392
  this.#abortReasons.add(reason);
8194
8393
  controller.abort(reason);
8195
8394
  });
@@ -8222,8 +8421,8 @@ var RunControl = class {
8222
8421
  /**
8223
8422
  * The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
8224
8423
  * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
8225
- * Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's or timeout's
8226
- * abort is reported as that abort, so a declared error keeps its own code.
8424
+ * Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
8425
+ * reported as that abort, so a declared error keeps its own code.
8227
8426
  */
8228
8427
  errorAfter(error) {
8229
8428
  if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
@@ -8266,10 +8465,11 @@ function globalModulesDir(prefix) {
8266
8465
  return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
8267
8466
  }
8268
8467
  function notGlobal(ctx, message) {
8269
- return new CliError({
8468
+ return runtimeError({
8270
8469
  code: NOT_GLOBAL_INSTALL,
8271
8470
  message,
8272
- hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.registry)}`
8471
+ hint: "install it with npm by running the Next step, so its update Command can replace it",
8472
+ next: [wholeLine(installCommandLine(ctx.registry))]
8273
8473
  });
8274
8474
  }
8275
8475
  /**
@@ -8404,10 +8604,6 @@ function renderUpdate(cli, install, update) {
8404
8604
  ...next
8405
8605
  ]).join("\n");
8406
8606
  }
8407
- async function skillNext(skill, latest, ctx) {
8408
- const { fix } = await skill.findingAt(latest, ctx);
8409
- return fix === void 0 ? [] : [fix];
8410
- }
8411
8607
  function interruptedAfterInstall(plan) {
8412
8608
  return new CliError({
8413
8609
  code: INTERRUPTED,
@@ -8418,7 +8614,6 @@ function interruptedAfterInstall(plan) {
8418
8614
  /** The built-in `update`, for a CLI installed globally with npm. */
8419
8615
  function updateCommand(cli, install, skill, { bin, node }) {
8420
8616
  return defineCommand$1({
8421
- name: "update",
8422
8617
  description: "install the latest version of this CLI from its registry",
8423
8618
  examples: [{
8424
8619
  command: `${cli.name} update`,
@@ -8447,7 +8642,7 @@ function updateCommand(cli, install, skill, { bin, node }) {
8447
8642
  latest,
8448
8643
  installed: null,
8449
8644
  upgrades: [],
8450
- next: await skillNext(skill, latest, ctx)
8645
+ next: await skill.nextAt(latest, ctx)
8451
8646
  };
8452
8647
  ctx.signal.throwIfAborted();
8453
8648
  ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
@@ -8460,7 +8655,7 @@ function updateCommand(cli, install, skill, { bin, node }) {
8460
8655
  prefix
8461
8656
  },
8462
8657
  upgrades: await upgradesOf(plan.packageDir, from, latest),
8463
- next: await skillNext(skill, latest, ctx)
8658
+ next: await skill.nextAt(latest, ctx)
8464
8659
  };
8465
8660
  if (ctx.signal.aborted) {
8466
8661
  ctx.deliver(update);
@@ -8505,7 +8700,7 @@ function checkOf(cli, install, { bin, node }) {
8505
8700
  function globalInstall(cli, install) {
8506
8701
  const adapter = {
8507
8702
  runAs: runAsOf(cli),
8508
- builtIns: (launch) => ({ update: updateCommand(cli, install, skillOf(cli, adapter), launch) }),
8703
+ builtIns: (launch) => ({ update: updateCommand(cli, install, skillOf$1(cli, adapter), launch) }),
8509
8704
  exitClasses: ["source"],
8510
8705
  check: (launch) => checkOf(cli, install, launch)
8511
8706
  };
@@ -8591,7 +8786,7 @@ function readProject(projectFile, ctx) {
8591
8786
  ok: true,
8592
8787
  project: projectOf(projectFile, ctx.cwd, {
8593
8788
  fileError: ctx.fileError,
8594
- callerLine: ctx.commandLine()
8789
+ rerun: ctx.rerun()
8595
8790
  })
8596
8791
  };
8597
8792
  } catch (error) {
@@ -8654,14 +8849,12 @@ function doctorChecks(cli, launch, read) {
8654
8849
  ...cli.project === void 0 ? [] : [projectRow(cli.project, read)],
8655
8850
  ...cli.doctor ?? [],
8656
8851
  ...toolRows(cli.tools ?? [], cli.commands),
8852
+ ...secretRows(cli.secrets ?? [], cli.commands),
8657
8853
  installRow(check),
8658
8854
  registryRow(cli, check),
8659
- ...skillOf(cli, install).rows
8855
+ ...skillOf$1(cli, install).rows
8660
8856
  ];
8661
8857
  }
8662
- //#endregion
8663
- //#region ../agent-cli/dist/built-ins/doctor.js
8664
- const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
8665
8858
  const DEFAULT_DEADLINE_MS = 1e4;
8666
8859
  const MS_PER_SECOND = 1e3;
8667
8860
  const MIN_LABEL_WIDTH = 8;
@@ -8732,24 +8925,33 @@ function render({ rows, next }, style) {
8732
8925
  return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
8733
8926
  }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
8734
8927
  }
8735
- function failure(cliName, callerLine, problems) {
8736
- const errors = problems.map(({ check, message, fix, code }) => new CliError({
8737
- code: code ?? "DOCTOR_PROBLEMS",
8738
- message: `${cliName} doctor found a problem in ${check}: ${message}`,
8739
- hint: fix === void 0 ? "the row says what is wrong" : `run \`${fix}\`, then run \`${callerLine}\` again`
8740
- }));
8928
+ function failure(cliName, rerun, problems) {
8929
+ const errors = problems.map(({ check, message, fix, code }) => {
8930
+ const head = {
8931
+ code: code ?? "DOCTOR_PROBLEMS",
8932
+ message: `${cliName} doctor found a problem in ${check}: ${message}`
8933
+ };
8934
+ return runtimeError(fix === void 0 ? {
8935
+ ...head,
8936
+ hint: "the row says what is wrong",
8937
+ next: []
8938
+ } : {
8939
+ ...head,
8940
+ hint: "run the Next steps in order",
8941
+ next: [wholeLine(fix), rerun]
8942
+ });
8943
+ });
8741
8944
  return new AggregateError(errors);
8742
8945
  }
8743
8946
  /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
8744
8947
  function doctorCommand(cli, launch) {
8745
8948
  return defineCommand$1({
8746
- name: "doctor",
8747
8949
  description: "check the setup and say what fixes each problem",
8748
8950
  examples: [{
8749
- command: `${runAsOf(cli)} doctor`,
8951
+ command: `${cli.name} doctor`,
8750
8952
  note: "check the setup and list the commands that fix it"
8751
8953
  }, {
8752
- command: `${runAsOf(cli)} doctor --json`,
8954
+ command: `${cli.name} doctor --json`,
8753
8955
  note: "get the rows and fixes as data"
8754
8956
  }],
8755
8957
  effect: "read",
@@ -8769,7 +8971,7 @@ function doctorCommand(cli, launch) {
8769
8971
  const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
8770
8972
  if (problems.length > 0) {
8771
8973
  ctx.deliver(report);
8772
- throw failure(cli.name, ctx.commandLine(), problems);
8974
+ throw failure(cli.name, ctx.rerun(), problems);
8773
8975
  }
8774
8976
  return report;
8775
8977
  },
@@ -8787,53 +8989,104 @@ function builtInCommands(cli, launch) {
8787
8989
  return {
8788
8990
  doctor: doctorCommand(cli, launch),
8789
8991
  ...install.builtIns(launch),
8790
- ...skillOf(cli, install).builtIns
8992
+ ...skillOf$1(cli, install).builtIns
8791
8993
  };
8792
8994
  }
8793
8995
  //#endregion
8794
- //#region ../agent-cli/dist/confirm.js
8795
- const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
8796
- const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
8797
- function checkConfirmation({ invocation, commandLine, given }, expected) {
8798
- if (given === expected) return;
8799
- const hint = `to confirm, run \`${commandLineOf(invocation, {
8800
- remove: [CONFIRM_FLAG.flag],
8801
- add: [CONFIRM_FLAG.flag, expected]
8802
- })}\``;
8803
- if (given === void 0) throw new UsageError({
8804
- code: CONFIRMATION_REQUIRED,
8805
- message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
8806
- hint
8807
- });
8808
- throw new UsageError({
8809
- code: CONFIRMATION_MISMATCH,
8810
- message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
8811
- hint
8812
- });
8996
+ //#region ../agent-cli/dist/command-spec.js
8997
+ /** The group's Commands and groups that help and suggestions may name: hidden Commands left out. */
8998
+ function visibleCommands({ commands }) {
8999
+ return Object.fromEntries(Object.entries(commands).filter(([, node]) => node.kind === "group" || !node.hidden));
8813
9000
  }
8814
- //#endregion
8815
- //#region ../agent-cli/dist/cost.js
8816
- const NOT_CONFIRMED = "NOT_CONFIRMED";
8817
- /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
8818
- function checkCost({ invocation, commandLine, yes }, cost) {
8819
- if (cost === void 0 || yes) return;
8820
- throw new CliError({
8821
- code: NOT_CONFIRMED,
8822
- message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
8823
- hint: `to approve the cost, run \`${commandLineOf(invocation, { add: [YES_FLAG.flag] })}\``
9001
+ /** The words that label a Command in errors and spill files: its path, or the CLI's name for a single-Command CLI. */
9002
+ function commandLabel(path, cliName) {
9003
+ return path.length === 0 ? [cliName] : path;
9004
+ }
9005
+ function declaredOf(command) {
9006
+ if (!hasPlan(command)) return {
9007
+ outputs: false,
9008
+ force: void 0,
9009
+ cost: false
9010
+ };
9011
+ return {
9012
+ outputs: command.outputs !== void 0,
9013
+ force: command.force,
9014
+ cost: command.cost !== void 0
9015
+ };
9016
+ }
9017
+ function withRunAs(example, { name, runAs }) {
9018
+ const { command } = example;
9019
+ return command === name || command.startsWith(`${name} `) ? {
9020
+ ...example,
9021
+ command: `${runAs}${command.slice(name.length)}`
9022
+ } : example;
9023
+ }
9024
+ function commandSpec(commandLine, command, { path, builtIn, names }) {
9025
+ const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
9026
+ return {
9027
+ kind: "command",
9028
+ commandLine,
9029
+ path,
9030
+ command,
9031
+ builtIn,
9032
+ description: command.description,
9033
+ help: command.help,
9034
+ hidden: command.hidden === true,
9035
+ operator: command.operator === true,
9036
+ effect: command.effect,
9037
+ examples: command.examples.map((example) => withRunAs(example, names)),
9038
+ args: argFields(command.args),
9039
+ options: optionFields(command.options),
9040
+ standardFlags: standardFlagsFor(command.effect, declaredOf(command), list),
9041
+ list,
9042
+ output: list?.output ?? command.output
9043
+ };
9044
+ }
9045
+ function group(commandLine, commands) {
9046
+ return {
9047
+ kind: "group",
9048
+ commandLine,
9049
+ commands
9050
+ };
9051
+ }
9052
+ /** The root group of a multi-Command CLI: the author's Commands, then the Built-in Commands. */
9053
+ function groupSpec(names, commands, builtIns) {
9054
+ const { runAs } = names;
9055
+ return group(runAs, {
9056
+ ...specsOf(runAs, commands, {
9057
+ path: [],
9058
+ builtIn: false,
9059
+ names
9060
+ }),
9061
+ ...specsOf(runAs, builtIns, {
9062
+ path: [],
9063
+ builtIn: true,
9064
+ names
9065
+ })
8824
9066
  });
8825
9067
  }
9068
+ function specsOf(commandLine, commands, { path, builtIn, names }) {
9069
+ return Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node, {
9070
+ path: [...path, key],
9071
+ builtIn,
9072
+ names
9073
+ })]));
9074
+ }
9075
+ function specOf(commandLine, node, place) {
9076
+ return isCommand(node) ? commandSpec(commandLine, node, place) : group(commandLine, specsOf(commandLine, node, place));
9077
+ }
8826
9078
  //#endregion
8827
- //#region ../agent-cli/dist/operator.js
8828
- const NEEDS_OPERATOR = "NEEDS_OPERATOR";
8829
- /** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
8830
- function checkOperator({ operator, commandLine }, env, callerLine) {
8831
- if (!operator || !isAgent(env)) return;
8832
- throw new CliError({
8833
- code: NEEDS_OPERATOR,
8834
- message: `\`${commandLine}\` must be run by a person at a terminal, not a Coding agent`,
8835
- hint: `ask the operator to type: ! ${callerLine}`
8836
- });
9079
+ //#region ../agent-cli/dist/error-table.js
9080
+ /** The Error table a CLI declares, empty when it declares none. */
9081
+ function errorTableOf({ errors }) {
9082
+ return errors ?? {};
9083
+ }
9084
+ /** The `CliError` for a code of `Table`, as `cliError` and `ctx.fail` build it. */
9085
+ function cliErrorOf(code, init, options) {
9086
+ return new CliError({
9087
+ ...init,
9088
+ code
9089
+ }, options);
8837
9090
  }
8838
9091
  //#endregion
8839
9092
  //#region ../agent-cli/dist/exit-classes.js
@@ -8849,43 +9102,48 @@ const EXIT_CODES = {
8849
9102
  bug: 70
8850
9103
  };
8851
9104
  const RUNTIME_EXIT_CLASSES = {
8852
- [INVALID_INPUT]: "caller",
8853
- [UNKNOWN_OPTION]: "caller",
8854
- [UNKNOWN_COMMAND]: "caller",
8855
- [UNKNOWN_FIELD]: "caller",
8856
- [CONFIRMATION_REQUIRED]: "caller",
8857
- [CONFIRMATION_MISMATCH]: "caller",
8858
- [NOT_CONFIRMED]: "operator",
8859
- [NEEDS_OPERATOR]: "operator",
8860
- [OUTPUT_EXISTS]: "caller",
8861
- [STDIN_STALLED]: "caller",
8862
- [INTERRUPTED]: "interrupted",
8863
- [DEADLINE_EXCEEDED]: "wait",
8864
- [INTERNAL]: "bug",
8865
- [PERMISSION_DENIED]: "operator",
8866
- [DISK_FULL]: "operator",
8867
- [NOT_FOUND]: "caller",
8868
- [ALREADY_EXISTS]: "caller",
8869
- [NOT_A_FILE]: "caller",
8870
- [TOOL_FAILED]: "operator",
8871
- [TOOL_MISSING]: "operator",
8872
- [PROJECT_INVALID]: "operator",
8873
- [DOCTOR_PROBLEMS]: "operator",
8874
- [NOT_GLOBAL_INSTALL]: "operator",
8875
- [REGISTRY_UNREACHABLE]: "operator",
8876
- [REGISTRY_AUTH]: "operator",
8877
- [INSTALL_FAILED]: "operator",
8878
- [NPM_FAILED]: "operator",
8879
- [NO_LATEST]: "source"
9105
+ INVALID_INPUT: "caller",
9106
+ UNKNOWN_OPTION: "caller",
9107
+ UNKNOWN_COMMAND: "caller",
9108
+ UNKNOWN_FIELD: "caller",
9109
+ CONFIRMATION_REQUIRED: "caller",
9110
+ CONFIRMATION_MISMATCH: "caller",
9111
+ NOT_CONFIRMED: "operator",
9112
+ NEEDS_OPERATOR: "operator",
9113
+ OUTPUT_EXISTS: "caller",
9114
+ STDIN_STALLED: "caller",
9115
+ INTERRUPTED: "interrupted",
9116
+ DEADLINE_EXCEEDED: "wait",
9117
+ INTERNAL: "bug",
9118
+ PERMISSION_DENIED: "operator",
9119
+ DISK_FULL: "operator",
9120
+ NOT_FOUND: "caller",
9121
+ ALREADY_EXISTS: "caller",
9122
+ NOT_A_FILE: "caller",
9123
+ TOOL_FAILED: "operator",
9124
+ TOOL_MISSING: "operator",
9125
+ SECRET_MISSING: "operator",
9126
+ PROJECT_INVALID: "operator",
9127
+ DOCTOR_PROBLEMS: "operator",
9128
+ NOT_GLOBAL_INSTALL: "operator",
9129
+ REGISTRY_UNREACHABLE: "operator",
9130
+ REGISTRY_AUTH: "operator",
9131
+ INSTALL_FAILED: "operator",
9132
+ NPM_FAILED: "operator",
9133
+ NO_LATEST: "source"
8880
9134
  };
9135
+ function isRuntimeCode(code) {
9136
+ return Object.hasOwn(RUNTIME_EXIT_CLASSES, code);
9137
+ }
8881
9138
  /** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
8882
9139
  function runtimeCodesIn(errors) {
8883
- return Object.keys(errors).filter((code) => Object.hasOwn(RUNTIME_EXIT_CLASSES, code));
9140
+ return Object.keys(errors).filter(isRuntimeCode);
8884
9141
  }
8885
9142
  function exitClassOfOne(error, errors) {
8886
9143
  if (!(error instanceof CliError)) return "bug";
8887
9144
  if (error instanceof UsageError) return "caller";
8888
- return RUNTIME_EXIT_CLASSES[error.code] ?? errors[error.code]?.class ?? "bug";
9145
+ if (isRuntimeCode(error.code)) return RUNTIME_EXIT_CLASSES[error.code];
9146
+ return errors[error.code]?.class ?? "bug";
8889
9147
  }
8890
9148
  function isBug(error, errors) {
8891
9149
  return exitClassOfOne(error, errors) === "bug";
@@ -8913,7 +9171,7 @@ function reachableExitClasses(cli) {
8913
9171
  "wait",
8914
9172
  ...cli.commands === void 0 ? [] : installOf(cli).exitClasses
8915
9173
  ];
8916
- const own = Object.values(cli.errors ?? {}).map((declaration) => declaration.class);
9174
+ const own = Object.values(errorTableOf(cli)).map((declaration) => declaration.class);
8917
9175
  return /* @__PURE__ */ new Set([...runtime, ...own]);
8918
9176
  }
8919
9177
  /** The signal named by an interrupt's abort reason; any other reason counts as Ctrl-C. */
@@ -8930,17 +9188,31 @@ function exitCodeOf(error, errors, stop) {
8930
9188
  }
8931
9189
  //#endregion
8932
9190
  //#region ../agent-cli/dist/agent-cli.js
8933
- function checkOwnCodes(cli) {
8934
- const redeclared = runtimeCodesIn(cli.errors ?? {});
8935
- if (redeclared.length > 0) throw new Error(`${cli.name} declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
9191
+ function checkOwnCodes(errors) {
9192
+ const redeclared = runtimeCodesIn(errors);
9193
+ if (redeclared.length > 0) throw new Error(`The Error table declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
8936
9194
  }
8937
- function defineCli$1(cli) {
8938
- checkOwnCodes(cli);
9195
+ function checkTree(commands, path, table) {
9196
+ for (const [key, node] of Object.entries(commands)) {
9197
+ const at = [...path, key];
9198
+ if (isCommand(node)) checkCommand(node, at.join(" "), table);
9199
+ else checkTree(node, at, table);
9200
+ }
9201
+ }
9202
+ function checkSkillPage({ name, skill }) {
9203
+ if (skill === void 0 || !("page" in skill)) return;
9204
+ if (!URL.canParse(skill.page) || new URL(skill.page).protocol !== "https:") throw new Error(`${name} declares skill.page ${skill.page}, which is not an https URL`);
9205
+ }
9206
+ function checkCli(cli) {
9207
+ const table = errorTableOf(cli);
9208
+ checkSkillPage(cli);
8939
9209
  if (cli.commands === void 0) {
8940
- if (skillOf(cli, installOf(cli)).kind === "packaged") throw new Error(`${cli.name} declares a Packaged skill, which only a CLI with several Commands can install`);
9210
+ checkCommand(cli.command, commandLabel([], cli.name).join(" "), table);
9211
+ if (skillOf$1(cli, installOf(cli)).kind === "packaged") throw new Error(`${cli.name} declares a Packaged skill, which only a CLI with several Commands can install`);
8941
9212
  return cli;
8942
9213
  }
8943
9214
  const { commands } = cli;
9215
+ checkTree(commands, [], table);
8944
9216
  const taken = Object.keys(builtInCommands(cli, {
8945
9217
  bin: void 0,
8946
9218
  node: void 0
@@ -8948,107 +9220,541 @@ function defineCli$1(cli) {
8948
9220
  if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
8949
9221
  return cli;
8950
9222
  }
8951
- /**
8952
- * Declares the CLI's ports and its Project file's settings once; the `defineCommand` and
8953
- * `defineCli` it returns type `ctx.ports` and `ctx.project`.
8954
- */
8955
- function agentCli() {
8956
- return {
8957
- defineCommand: defineCommandFor(),
8958
- defineCli: defineCli$1
8959
- };
8960
- }
8961
- //#endregion
8962
- //#region ../agent-cli/dist/error-table.js
8963
- /**
8964
- * Keeps each code's literal class and `details` schema, so `cliError` can type its throw sites;
8965
- * `print` must name a string key of the code's `details`.
8966
- */
8967
- function defineErrors(table) {
8968
- return table;
8969
- }
8970
- /**
8971
- * A `CliError` for a code of `table`, whose `details` must match the code's schema. The `hint`
8972
- * names the next command to run and says whether running it again can help; `retryAt` (ISO 8601)
8973
- * is allowed only on a wait-class code.
8974
- */
8975
- function cliError(_table, code, init, options) {
8976
- return new CliError({
8977
- ...init,
8978
- code
8979
- }, options);
8980
- }
8981
- //#endregion
8982
- //#region ../agent-cli/dist/command-spec.js
8983
- /** The group's Commands and groups that help and suggestions may name: hidden Commands left out. */
8984
- function visibleCommands({ commands }) {
8985
- return Object.fromEntries(Object.entries(commands).filter(([, node]) => node.kind === "group" || !node.hidden));
9223
+ function nameOf({ name, bin }) {
9224
+ const bins = typeof bin === "object" ? Object.keys(bin) : [];
9225
+ const [only] = bins;
9226
+ return bins.length === 1 && only !== void 0 ? only : name.replace(/^@[^/]+\//, "");
8986
9227
  }
8987
- function declaredOf(command) {
8988
- if (!hasPlan(command)) return {
8989
- outputs: false,
8990
- force: void 0,
8991
- cost: false
9228
+ function installFrom(name, manifest, install) {
9229
+ if (install === "dependency") return {
9230
+ mode: "dependency",
9231
+ package: manifest.name
8992
9232
  };
8993
- return {
8994
- outputs: command.outputs !== void 0,
8995
- force: command.force,
8996
- cost: command.cost !== void 0
9233
+ if (install !== "global") return {
9234
+ mode: "dependency",
9235
+ package: manifest.name,
9236
+ lockstep: install.lockstep
8997
9237
  };
8998
- }
8999
- const SINGLE_COMMAND = {
9000
- path: [],
9001
- builtIn: false
9002
- };
9003
- function commandSpec(commandLine, command, { path, builtIn } = SINGLE_COMMAND) {
9004
- const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
9238
+ const registry = manifest.publishConfig?.registry;
9239
+ if (registry === void 0) throw new Error(`${name} declares a Global install, which needs publishConfig.registry in its package.json`);
9005
9240
  return {
9006
- kind: "command",
9007
- commandLine,
9008
- path,
9009
- command,
9010
- builtIn,
9011
- description: command.description,
9012
- help: command.help,
9013
- hidden: command.hidden === true,
9014
- operator: command.operator === true,
9015
- effect: command.effect,
9016
- examples: command.examples,
9017
- args: argFields(command.args),
9018
- options: optionFields(command.options),
9019
- standardFlags: standardFlagsFor(command.effect, declaredOf(command), list),
9020
- list,
9021
- output: list?.output ?? command.output
9241
+ mode: "global",
9242
+ package: manifest.name,
9243
+ registry
9022
9244
  };
9023
9245
  }
9024
- function group(commandLine, commands) {
9246
+ function resolved(cli) {
9247
+ const { manifest, ...declared } = cli;
9248
+ const name = nameOf(manifest);
9249
+ const about = {
9250
+ name,
9251
+ version: manifest.version,
9252
+ description: manifest.description
9253
+ };
9254
+ if (declared.commands === void 0) return {
9255
+ ...declared,
9256
+ ...about
9257
+ };
9258
+ const { install, ...rest } = declared;
9025
9259
  return {
9026
- kind: "group",
9027
- commandLine,
9028
- commands
9260
+ ...rest,
9261
+ ...about,
9262
+ ...manifest.engines === void 0 ? {} : { engines: manifest.engines },
9263
+ ...install === void 0 ? {} : { install: installFrom(name, manifest, install) }
9029
9264
  };
9030
9265
  }
9031
- /** The root group of a multi-Command CLI: the author's Commands, then the Built-in Commands. */
9032
- function groupSpec(commandLine, commands, builtIns) {
9033
- return group(commandLine, {
9034
- ...specsOf(commandLine, commands, {
9035
- path: [],
9036
- builtIn: false
9266
+ /**
9267
+ * Takes the CLI's ports factory, Project file and Error table once; the `defineCommand`,
9268
+ * `defineCli` and `cliError` it returns are typed by them.
9269
+ */
9270
+ function agentCli(values = {}) {
9271
+ checkOwnCodes(errorTableOf(values));
9272
+ return {
9273
+ defineCommand: defineCommandFor(),
9274
+ defineCli: (cli) => checkCli({
9275
+ ...resolved(cli),
9276
+ ...values
9037
9277
  }),
9038
- ...specsOf(commandLine, builtIns, {
9039
- path: [],
9040
- builtIn: true
9041
- })
9042
- });
9043
- }
9044
- function specsOf(commandLine, commands, { path, builtIn }) {
9045
- return Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node, {
9046
- path: [...path, key],
9047
- builtIn
9048
- })]));
9049
- }
9050
- function specOf(commandLine, node, place) {
9051
- return isCommand(node) ? commandSpec(commandLine, node, place) : group(commandLine, specsOf(commandLine, node, place));
9278
+ cliError: cliErrorOf
9279
+ };
9280
+ }
9281
+ //#endregion
9282
+ //#region ../agent-cli/dist/context/side-task.js
9283
+ /** How long a run waits for its Side task once its output is written. */
9284
+ const SIDE_TASK_WAIT_MS = 1e3;
9285
+ const noSideTask = async () => {};
9286
+ function waitOver() {
9287
+ return new DOMException(`the side task ran past its ${SIDE_TASK_WAIT_MS} ms wait`, "TimeoutError");
9288
+ }
9289
+ function startSideTask(sideTask, runWith, { clock, failed }) {
9290
+ if (sideTask === void 0) return noSideTask;
9291
+ const controller = new AbortController();
9292
+ const { signal } = controller;
9293
+ const running = (async () => sideTask(runWith(signal)))().catch((error) => {
9294
+ if (!(signal.aborted && error === signal.reason)) failed(error);
9295
+ });
9296
+ return async () => {
9297
+ const waited = new AbortController();
9298
+ const capped = new Promise((resolve) => after(clock, SIDE_TASK_WAIT_MS, waited.signal, () => {
9299
+ const over = waitOver();
9300
+ failed(over);
9301
+ controller.abort(over);
9302
+ resolve();
9303
+ }));
9304
+ await Promise.race([running, capped]);
9305
+ waited.abort();
9306
+ };
9307
+ }
9308
+ //#endregion
9309
+ //#region ../agent-cli/dist/command-line/suggest.js
9310
+ const MAX_DISTANCE = 3;
9311
+ const MIN_SIMILARITY = .4;
9312
+ function distance(a, b) {
9313
+ const width = b.length + 1;
9314
+ const d = Array.from({ length: (a.length + 1) * width }, (_, k) => {
9315
+ const i = Math.floor(k / width);
9316
+ const j = k % width;
9317
+ return i === 0 || j === 0 ? i + j : 0;
9318
+ });
9319
+ const at = (i, j) => d[i * width + j] ?? 0;
9320
+ for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
9321
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
9322
+ const edit = Math.min(at(i - 1, j) + 1, at(i, j - 1) + 1, at(i - 1, j - 1) + cost);
9323
+ const transposed = i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1];
9324
+ d[i * width + j] = transposed ? Math.min(edit, at(i - 2, j - 2) + 1) : edit;
9325
+ }
9326
+ return at(a.length, b.length);
9327
+ }
9328
+ function isClose(word, candidate) {
9329
+ const length = Math.max(word.length, candidate.length);
9330
+ const edits = distance(word, candidate);
9331
+ return candidate.length > 1 && edits < MAX_DISTANCE && (length - edits) / length > MIN_SIMILARITY;
9332
+ }
9333
+ /** The names closest to `word`, sorted, several when they tie, or none when nothing is close. */
9334
+ function closestNames(word, names) {
9335
+ const close = [...new Set(names)].filter((candidate) => isClose(word, candidate));
9336
+ const best = Math.min(...close.map((candidate) => distance(word, candidate)));
9337
+ return close.filter((candidate) => distance(word, candidate) === best).sort((a, b) => a.localeCompare(b));
9338
+ }
9339
+ /** The long flags closest to `flag`, sorted, or none for a short flag or when nothing is close. */
9340
+ function closestFlags(flag, flags) {
9341
+ if (!flag.startsWith("--")) return [];
9342
+ const names = flags.map((candidate) => candidate.slice(2));
9343
+ return closestNames(flag.slice(2), names).map((name) => `--${name}`);
9344
+ }
9345
+ /** The one long flag closest to `flag`, or none when nothing is close or several tie. */
9346
+ function closestFlag(flag, flags) {
9347
+ const closest = closestFlags(flag, flags);
9348
+ return closest.length === 1 ? closest[0] : void 0;
9349
+ }
9350
+ //#endregion
9351
+ //#region ../agent-cli/dist/command-line/parse-input.js
9352
+ const NO_INPUT = object({});
9353
+ const WHITESPACE = /\s/;
9354
+ function knownFlags(spec) {
9355
+ return [...spec.options.map((field) => ({
9356
+ flag: field.label,
9357
+ takes: field.type === "boolean" ? "nothing" : "optional value"
9358
+ })), ...spec.standardFlags.map(({ flag, takes }) => ({
9359
+ flag,
9360
+ takes
9361
+ }))];
9362
+ }
9363
+ function candidateFlags(known) {
9364
+ return [
9365
+ ...known.map(({ flag }) => flag),
9366
+ VERSION_FLAG,
9367
+ HELP_FLAG
9368
+ ];
9369
+ }
9370
+ function unknownOptionsOf(words, candidates) {
9371
+ return words.map((word) => {
9372
+ const flag = word.split("=")[0] ?? word;
9373
+ return {
9374
+ flag,
9375
+ suggestion: closestFlag(flag, candidates)
9376
+ };
9377
+ });
9378
+ }
9379
+ function optionValue(field, all) {
9380
+ if (field.variadic) return variadicValue(field, (all ?? []).filter((value) => value !== true));
9381
+ const raw = all?.[all.length - 1];
9382
+ return field.type === "boolean" && raw === true && field.label === `--no-${field.name}` ? false : raw;
9383
+ }
9384
+ function converted(field, raw) {
9385
+ if (Array.isArray(raw)) return raw.map((element) => converted(field, element));
9386
+ if (field.type !== "number" || typeof raw !== "string" || raw.trim() === "") return raw;
9387
+ const number = Number(raw);
9388
+ return Number.isNaN(number) ? raw : number;
9389
+ }
9390
+ function variadicValue(field, values) {
9391
+ return values.length === 0 && !field.required ? void 0 : values;
9392
+ }
9393
+ function parse(schema, values) {
9394
+ const input = Object.fromEntries([...values].filter(([, raw]) => raw !== void 0).map(([field, raw]) => [field.key, converted(field, raw)]));
9395
+ return schema.safeParse(input);
9396
+ }
9397
+ function issueLines(result, values, { howToPass, arrayIssueLine, reported = [], fromStdin }) {
9398
+ if (result.success) return [];
9399
+ return result.error.issues.flatMap((issue) => {
9400
+ const field = [...values.keys()].find((candidate) => candidate.key === issue.path[0]);
9401
+ if (field === void 0) return [` ${issue.message}`];
9402
+ if (reported.includes(field)) return [];
9403
+ const raw = values.get(field);
9404
+ if (raw === void 0) return [` ${field.label}: required, ${howToPass(field)}`];
9405
+ if (field === fromStdin) return [` ${field.label} from stdin: ${issue.message}`];
9406
+ return Array.isArray(raw) ? [arrayIssueLine(field, raw, issue)] : [` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`];
9407
+ });
9408
+ }
9409
+ function flagValuesIssueLine(field, raw, issue) {
9410
+ const [, element] = issue.path;
9411
+ if (typeof element === "number") return ` ${field.label} ${JSON.stringify(raw[element])}: ${issue.message}`;
9412
+ return raw.length === 0 ? ` ${field.label}: ${issue.message}, pass ${field.usage}` : ` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`;
9413
+ }
9414
+ function operandsIssueLine(field, raw, issue, first) {
9415
+ const [, element] = issue.path;
9416
+ if (typeof element === "number") return ` ${field.label} ${JSON.stringify(raw[element])} (argument ${first + element}): ${issue.message}`;
9417
+ return raw.length === 0 ? ` ${field.label}: ${issue.message}, pass them as arguments from ${first}` : ` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`;
9418
+ }
9419
+ function unknownLine({ flag, suggestion }) {
9420
+ return ` unknown option '${flag}'${suggestion === void 0 ? "" : ` (did you mean ${suggestion}?)`}`;
9421
+ }
9422
+ function invalidInput(commandLine, invocation, unknown, otherLines) {
9423
+ const renames = new Map(unknown.flatMap(({ flag, suggestion }) => suggestion === void 0 ? [] : [[flag, suggestion]]));
9424
+ const correctable = otherLines.length === 0 && renames.size === unknown.length;
9425
+ return new UsageError({
9426
+ code: INVALID_INPUT,
9427
+ message: [
9428
+ `invalid input for \`${commandLine}\``,
9429
+ ...unknown.map(unknownLine),
9430
+ ...otherLines
9431
+ ].join("\n"),
9432
+ hint: correctable ? `did you mean \`${commandLineRenaming(invocation, renames)}\`?` : `run \`${commandLine} --help\` to see the expected input`
9433
+ });
9434
+ }
9435
+ function stdinFieldOf(spec, operands) {
9436
+ const index = spec.args.findIndex((field) => field.stdin);
9437
+ return operands[index]?.value === "-" ? spec.args[index] : void 0;
9438
+ }
9439
+ /** The name of the routed Command's arg given `-`, which takes its text from stdin. */
9440
+ function stdinArgOf({ spec, words }) {
9441
+ return stdinFieldOf(spec, tokenize(words, knownFlags(spec)).operands)?.name;
9442
+ }
9443
+ async function stdinTextOf(spec, operands, read) {
9444
+ const field = stdinFieldOf(spec, operands);
9445
+ return field === void 0 ? void 0 : {
9446
+ field,
9447
+ text: await read(field)
9448
+ };
9449
+ }
9450
+ async function parseInput(spec, words, invocation, readStdin) {
9451
+ const { command, args: argFields, options: optionFields } = spec;
9452
+ const pageFields = spec.list?.pageFields ?? [];
9453
+ const known = knownFlags(spec);
9454
+ const { operands, unknownOptions, values, occurrences } = tokenize(words, known);
9455
+ const stdin = await stdinTextOf(spec, operands, readStdin);
9456
+ if (values.get(CONFIRM_FLAG.flag) === true) throw invalidInputError(spec.commandLine, `option '${CONFIRM_FLAG.usage}' argument missing`);
9457
+ const missing = [...optionFields, ...pageFields].filter((field) => field.type !== "boolean" && (field.variadic ? occurrences.get(field.label)?.includes(true) : values.get(field.label) === true));
9458
+ const argValue = (field, index) => {
9459
+ if (field === stdin?.field) return stdin.text;
9460
+ if (!field.variadic) return operands[index]?.value;
9461
+ return variadicValue(field, operands.slice(index).filter((operand) => !operand.afterUnknownOption).map((operand) => operand.value));
9462
+ };
9463
+ const argValues = new Map(argFields.map((field, index) => [field, argValue(field, index)]));
9464
+ const extraOperands = argFields.some((field) => field.variadic) ? [] : operands.slice(argFields.length);
9465
+ const valuesOf = (fields) => new Map(fields.map((field) => [field, missing.includes(field) ? void 0 : optionValue(field, occurrences.get(field.label))]));
9466
+ const flagWording = {
9467
+ howToPass: (field) => `pass ${field.usage}`,
9468
+ arrayIssueLine: flagValuesIssueLine,
9469
+ reported: missing
9470
+ };
9471
+ const optionValues = valuesOf(optionFields);
9472
+ const pageValues = valuesOf(pageFields);
9473
+ const args = parse(command.args ?? NO_INPUT, argValues);
9474
+ const options = parse(command.options ?? NO_INPUT, optionValues);
9475
+ const page = parse(PAGE_INPUT, pageValues);
9476
+ const unknown = unknownOptionsOf(unknownOptions, candidateFlags(known));
9477
+ const otherLines = [
9478
+ ...missing.map((field) => ` ${field.label}: missing value, pass ${field.usage}`),
9479
+ ...extraOperands.filter((operand) => !operand.afterUnknownOption).map((operand) => ` unexpected argument ${JSON.stringify(operand.value)}`),
9480
+ ...issueLines(args, argValues, {
9481
+ howToPass: (field) => `pass it as argument ${argFields.indexOf(field) + 1}`,
9482
+ arrayIssueLine: (field, raw, issue) => operandsIssueLine(field, raw, issue, argFields.indexOf(field) + 1),
9483
+ fromStdin: stdin?.field
9484
+ }),
9485
+ ...issueLines(options, optionValues, flagWording),
9486
+ ...issueLines(page, pageValues, flagWording)
9487
+ ];
9488
+ if (unknown.length === 0 && otherLines.length === 0 && args.success && options.success && page.success) return {
9489
+ input: {
9490
+ ...args.data,
9491
+ ...options.data
9492
+ },
9493
+ page: page.data,
9494
+ values
9495
+ };
9496
+ const [onlyWord] = unknownOptions;
9497
+ if (onlyWord !== void 0 && unknownOptions.length === 1 && WHITESPACE.test(onlyWord) && argFields.some((field) => field.stdin && argValues.get(field) === void 0)) throw stdinTextError(spec.commandLine, invocation, onlyWord);
9498
+ const [only] = unknown;
9499
+ if (only !== void 0 && unknown.length === 1 && otherLines.length === 0) throw unknownOptionError(spec.commandLine, invocation, only.flag, only.suggestion === void 0 ? [] : [only.suggestion]);
9500
+ throw invalidInput(spec.commandLine, invocation, unknown, otherLines);
9501
+ }
9502
+ /**
9503
+ * The validated input and Standard flag values of the routed Command, or one usage error; the arg
9504
+ * given `-` takes its text from `readStdin`.
9505
+ */
9506
+ async function parseCommand({ spec, words, json }, invocation, readStdin) {
9507
+ const { input, page, values } = await parseInput(spec, words, invocation, readStdin);
9508
+ const confirm = values.get(CONFIRM_FLAG.flag);
9509
+ return {
9510
+ input,
9511
+ flags: {
9512
+ json,
9513
+ dryRun: values.get(DRY_RUN_FLAG.flag) === true,
9514
+ confirm: typeof confirm === "string" ? confirm : void 0,
9515
+ yes: values.get(YES_FLAG.flag) === true,
9516
+ force: values.get(FORCE_FLAG.flag) === true,
9517
+ page,
9518
+ fields: spec.list === void 0 ? void 0 : selectedFields(values, spec.list.rowFields, spec.commandLine)
9519
+ }
9520
+ };
9521
+ }
9522
+ //#endregion
9523
+ //#region ../agent-cli/dist/command-line/route.js
9524
+ const GROUP_FLAGS = [
9525
+ HELP_FLAG,
9526
+ VERSION_FLAG,
9527
+ JSON_FLAG.flag
9528
+ ];
9529
+ function beforeEnd(words) {
9530
+ const end = words.indexOf("--");
9531
+ return end === -1 ? words : words.slice(0, end);
9532
+ }
9533
+ function wantsHelp(words) {
9534
+ return beforeEnd(words).some((word) => word === "--help" || word === "-h");
9535
+ }
9536
+ function childOf(group, name) {
9537
+ return Object.hasOwn(group.commands, name) ? group.commands[name] : void 0;
9538
+ }
9539
+ function isFlagLike(word) {
9540
+ return word.length > 1 && word[0] === "-";
9541
+ }
9542
+ function walk(node, words, json, invocation) {
9543
+ const help = {
9544
+ kind: "help",
9545
+ node,
9546
+ json
9547
+ };
9548
+ if (node.kind === "command") return wantsHelp(words) ? help : {
9549
+ kind: "command",
9550
+ spec: node,
9551
+ words,
9552
+ json
9553
+ };
9554
+ const [first, ...rest] = words;
9555
+ if (first === void 0) return help;
9556
+ const child = childOf(node, first);
9557
+ if (child !== void 0) return walk(child, rest, json, invocation);
9558
+ if (first === "--") {
9559
+ const [name, ...after] = rest;
9560
+ if (name === void 0) return help;
9561
+ const named = childOf(node, name);
9562
+ if (named !== void 0) return walk(named, ["--", ...after], json, invocation);
9563
+ return unknownCommand(node, name, json);
9564
+ }
9565
+ if (wantsHelp(words)) return help;
9566
+ if (isFlagLike(first)) {
9567
+ const suggestions = closestFlags(first, GROUP_FLAGS);
9568
+ return {
9569
+ kind: "usage error",
9570
+ error: unknownOptionError(node.commandLine, invocation, first, suggestions),
9571
+ node,
9572
+ json
9573
+ };
9574
+ }
9575
+ return unknownCommand(node, first, json);
9576
+ }
9577
+ function unknownCommand(group, name, json) {
9578
+ const suggestions = closestNames(name, Object.keys(visibleCommands(group)));
9579
+ return {
9580
+ kind: "usage error",
9581
+ error: unknownCommandError(group.commandLine, name, suggestions),
9582
+ node: group,
9583
+ json
9584
+ };
9585
+ }
9586
+ /** Walks the caller's command line through the Commands to the one it names, or to a stop. */
9587
+ function route(root, invocation) {
9588
+ const { argv } = invocation;
9589
+ const options = beforeEnd(argv);
9590
+ const json = options.includes(JSON_FLAG.flag);
9591
+ if (options.includes("--version")) return {
9592
+ kind: "version",
9593
+ json
9594
+ };
9595
+ return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
9596
+ }
9597
+ //#endregion
9598
+ //#region ../agent-cli/dist/command-line/route-line.js
9599
+ /** Routes `argv`, the line's words after the Run-as, to a Command of `root`. */
9600
+ function routeLine(root, runAs, argv) {
9601
+ if (!argv.ok) return {
9602
+ ok: false,
9603
+ reason: `it does not tokenize: ${argv.reason}`
9604
+ };
9605
+ const invocation = {
9606
+ runAs,
9607
+ argv: argv.words,
9608
+ flags: []
9609
+ };
9610
+ const routed = route(root, invocation);
9611
+ switch (routed.kind) {
9612
+ case "usage error": return {
9613
+ ok: false,
9614
+ reason: routed.error.message
9615
+ };
9616
+ case "help":
9617
+ case "version": return {
9618
+ ok: false,
9619
+ reason: "it names no Command"
9620
+ };
9621
+ case "command": return {
9622
+ ok: true,
9623
+ routed,
9624
+ invocation
9625
+ };
9626
+ }
9627
+ }
9628
+ //#endregion
9629
+ //#region ../agent-cli/dist/command-line/shell-words.js
9630
+ const BLANK = /\s/;
9631
+ const SHELL_SYNTAX = /* @__PURE__ */ new Set([
9632
+ "|",
9633
+ "&",
9634
+ ";",
9635
+ ">",
9636
+ "(",
9637
+ ")",
9638
+ "$",
9639
+ "`"
9640
+ ]);
9641
+ const STDIN_REDIRECT = "<";
9642
+ const DOUBLE_QUOTE_ESCAPES = /* @__PURE__ */ new Set([
9643
+ "$",
9644
+ "`",
9645
+ "\"",
9646
+ "\\"
9647
+ ]);
9648
+ function quotedFrom(line, start, quote) {
9649
+ let word = "";
9650
+ for (let index = start; index < line.length; index += 1) {
9651
+ const char = line[index] ?? "";
9652
+ if (char === quote) return {
9653
+ word,
9654
+ end: index + 1
9655
+ };
9656
+ const next = line[index + 1];
9657
+ if (quote === "\"" && char === "\\" && next !== void 0 && DOUBLE_QUOTE_ESCAPES.has(next)) {
9658
+ word += next;
9659
+ index += 1;
9660
+ } else word += char;
9661
+ }
9662
+ }
9663
+ function skipBlanks(line, start) {
9664
+ let index = start;
9665
+ while (BLANK.test(line[index] ?? "")) index += 1;
9666
+ return index;
9667
+ }
9668
+ function isBoundary(char) {
9669
+ return BLANK.test(char) || char === STDIN_REDIRECT || SHELL_SYNTAX.has(char);
9670
+ }
9671
+ function wordFrom(line, start) {
9672
+ let word = "";
9673
+ let index = start;
9674
+ while (index < line.length && !isBoundary(line[index] ?? "")) {
9675
+ const char = line[index] ?? "";
9676
+ if (char === "'" || char === "\"") {
9677
+ const quoted = quotedFrom(line, index + 1, char);
9678
+ if (quoted === void 0) return { reason: `its ${char} is never closed` };
9679
+ word += quoted.word;
9680
+ index = quoted.end;
9681
+ } else if (char === "\\") {
9682
+ word += line[index + 1] ?? "";
9683
+ index += 2;
9684
+ } else {
9685
+ word += char;
9686
+ index += 1;
9687
+ }
9688
+ }
9689
+ return {
9690
+ word,
9691
+ end: index
9692
+ };
9693
+ }
9694
+ /**
9695
+ * The words a POSIX shell would pass for `line`, without the file of a `< file` redirect; a reason
9696
+ * instead when a quote is left open or the line uses other shell syntax.
9697
+ */
9698
+ function shellWords(line) {
9699
+ const words = [];
9700
+ let index = skipBlanks(line, 0);
9701
+ while (index < line.length) {
9702
+ const redirect = line[index] === STDIN_REDIRECT;
9703
+ if (redirect) index = skipBlanks(line, index + 1);
9704
+ const char = line[index];
9705
+ if (char === void 0 || char === STDIN_REDIRECT) return {
9706
+ ok: false,
9707
+ reason: "its `<` names no file"
9708
+ };
9709
+ if (SHELL_SYNTAX.has(char)) return {
9710
+ ok: false,
9711
+ reason: `it uses the shell's \`${char}\``
9712
+ };
9713
+ const scanned = wordFrom(line, index);
9714
+ if ("reason" in scanned) return {
9715
+ ok: false,
9716
+ reason: scanned.reason
9717
+ };
9718
+ if (!redirect) words.push(scanned.word);
9719
+ index = skipBlanks(line, scanned.end);
9720
+ }
9721
+ return {
9722
+ ok: true,
9723
+ words
9724
+ };
9725
+ }
9726
+ //#endregion
9727
+ //#region ../agent-cli/dist/check-next-steps.js
9728
+ function argvOf(words, runAs) {
9729
+ const line = shellWords(lineOf(words, runAs));
9730
+ const lead = shellWords(runAs);
9731
+ if (!lead.ok) return lead;
9732
+ if (!line.ok) return line;
9733
+ return {
9734
+ ok: true,
9735
+ words: line.words.slice(lead.words.length)
9736
+ };
9737
+ }
9738
+ /**
9739
+ * Why `step` would not run a Command of `root`, or `undefined` when it would. A line given whole is
9740
+ * not checked, since it may run another program.
9741
+ */
9742
+ function nextStepProblem(root, runAs, step) {
9743
+ const words = wordsOf(step);
9744
+ if (words === void 0) return void 0;
9745
+ const line = routeLine(root, runAs, argvOf(words, runAs));
9746
+ if (!line.ok) return line.reason;
9747
+ const { routed } = line;
9748
+ const { unknownOptions } = tokenize(routed.words, knownFlags(routed.spec));
9749
+ return unknownOptions.length === 0 ? void 0 : `unknown option ${unknownOptions.join(", ")}`;
9750
+ }
9751
+ /** `error`, or a bug naming each of its Next steps that would not run. */
9752
+ function checkedNextSteps(error, check, runAs) {
9753
+ const problems = errorsOf(error).flatMap((leaf) => leaf instanceof CliError ? leaf.next.flatMap((step) => {
9754
+ const problem = check(step);
9755
+ return problem === void 0 ? [] : [`\`${lineOf(step, runAs)}\`, a Next step of ${leaf.code}: ${problem}`];
9756
+ }) : []);
9757
+ return problems.length === 0 ? error : new Error(`a Next step would not run: ${problems.join("; ")}`, { cause: error });
9052
9758
  }
9053
9759
  //#endregion
9054
9760
  //#region ../agent-cli/dist/help.js
@@ -9172,7 +9878,7 @@ function inputSchema(schema) {
9172
9878
  function optionsSchema(options) {
9173
9879
  return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
9174
9880
  }
9175
- function commandData(spec) {
9881
+ function commandData(spec, table) {
9176
9882
  return {
9177
9883
  name: spec.commandLine,
9178
9884
  description: spec.description,
@@ -9180,6 +9886,11 @@ function commandData(spec) {
9180
9886
  effect: spec.effect,
9181
9887
  operator: spec.operator,
9182
9888
  examples: spec.examples,
9889
+ secrets: spec.command.secrets ?? [],
9890
+ errors: (spec.command.errors ?? []).map((code) => ({
9891
+ code,
9892
+ class: table[code]?.class
9893
+ })),
9183
9894
  args: inputSchema(spec.command.args ?? object({})),
9184
9895
  options: optionsSchema(spec.options),
9185
9896
  standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
@@ -9190,13 +9901,13 @@ function commandData(spec) {
9190
9901
  output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
9191
9902
  };
9192
9903
  }
9193
- function nodeData(node) {
9194
- return node.kind === "command" ? commandData(node) : groupData(node);
9904
+ function nodeData(node, table) {
9905
+ return node.kind === "command" ? commandData(node, table) : groupData(node, table);
9195
9906
  }
9196
- function groupData(group) {
9907
+ function groupData(group, table) {
9197
9908
  return {
9198
9909
  name: group.commandLine,
9199
- commands: Object.values(visibleCommands(group)).map(nodeData)
9910
+ commands: Object.values(visibleCommands(group)).map((node) => nodeData(node, table))
9200
9911
  };
9201
9912
  }
9202
9913
  function printed$1(data) {
@@ -9208,21 +9919,31 @@ function errorsData(errors) {
9208
9919
  details: declaration.details === void 0 ? null : toJSONSchema(declaration.details, { unrepresentable: "any" })
9209
9920
  }]));
9210
9921
  }
9211
- function cliHelpJson({ name, version, description, errors }, root) {
9212
- return printed$1({
9922
+ function publishedTable(table, root) {
9923
+ if (root.kind !== "command") return table;
9924
+ const declared = new Set(root.command.errors ?? []);
9925
+ return Object.fromEntries(Object.entries(table).filter(([code]) => declared.has(code)));
9926
+ }
9927
+ function cliHelpJson(cli, root) {
9928
+ const { name, version, description } = cli;
9929
+ const header = {
9213
9930
  name,
9214
9931
  version,
9215
- description,
9216
- ...nodeData(root),
9932
+ description
9933
+ };
9934
+ const table = errorTableOf(cli);
9935
+ return printed$1({
9936
+ ...header,
9937
+ ...nodeData(root, table),
9217
9938
  name,
9218
- errors: errorsData(errors ?? {})
9939
+ errors: errorsData(publishedTable(table, root))
9219
9940
  });
9220
9941
  }
9221
- function groupHelpJson(group) {
9222
- return printed$1(groupData(group));
9942
+ function groupHelpJson(group, table) {
9943
+ return printed$1(groupData(group, table));
9223
9944
  }
9224
- function commandHelpJson(spec) {
9225
- return printed$1(commandData(spec));
9945
+ function commandHelpJson(spec, table) {
9946
+ return printed$1(commandData(spec, table));
9226
9947
  }
9227
9948
  //#endregion
9228
9949
  //#region ../agent-cli/dist/output.js
@@ -9231,14 +9952,14 @@ function withTrailingNewline(text) {
9231
9952
  return text === "" || text.endsWith("\n") ? text : `${text}\n`;
9232
9953
  }
9233
9954
  function printed(value, render, { json, invocation, style, fields }) {
9234
- if (!(value instanceof Page)) return json ? `${JSON.stringify(value)}\n` : withTrailingNewline(render(style));
9955
+ if (!(value instanceof Page)) return json ? `${JSON.stringify(value ?? null)}\n` : withTrailingNewline(render(style));
9235
9956
  const page = printedPage(value, invocation);
9236
- if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
9957
+ if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page, style)}`;
9237
9958
  const rows = projected(page.items, fields);
9238
9959
  return json ? `${JSON.stringify({
9239
9960
  ...page,
9240
9961
  items: rows
9241
- })}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
9962
+ })}\n` : `${renderRows(rows, fields)}${pageFooter(page, style)}`;
9242
9963
  }
9243
9964
  /** A progress message as stderr carries it: one JSON object per line under `--json` (ADR 0006). */
9244
9965
  function progressLine(message, json) {
@@ -9254,6 +9975,7 @@ function stderrLine(key, text, json) {
9254
9975
  function errorText(error, format) {
9255
9976
  const text = formatRuntimeError(error, {
9256
9977
  ...format,
9978
+ runAs: format.invocation.runAs,
9257
9979
  isBug: (each) => isBug(each, format.errors)
9258
9980
  });
9259
9981
  const { usageHelp, json } = format;
@@ -9277,10 +9999,11 @@ const STOPPED_RESULT = {
9277
9999
  exitCode: SUCCESS_EXIT_CODE
9278
10000
  };
9279
10001
  function errorResult(error, format) {
10002
+ const checked = format.checkNextStep === void 0 ? error : checkedNextSteps(error, format.checkNextStep, format.invocation.runAs);
9280
10003
  return {
9281
10004
  stdout: "",
9282
- stderr: errorText(error, format),
9283
- exitCode: exitCodeOf(error, format.errors, format.stop)
10005
+ stderr: errorText(checked, format),
10006
+ exitCode: exitCodeOf(checked, format.errors, format.stop)
9284
10007
  };
9285
10008
  }
9286
10009
  async function commandResult(settled, format, spill) {
@@ -9294,12 +10017,18 @@ async function commandResult(settled, format, spill) {
9294
10017
  return errorResult(spillError, format);
9295
10018
  }
9296
10019
  const { error, delivered } = settled;
10020
+ if (delivered === void 0) return errorResult(error, format);
10021
+ let outcome;
10022
+ try {
10023
+ outcome = delivered();
10024
+ } catch (rejected) {
10025
+ return errorResult(new AggregateError([...errorsOf(error), rejected]), format);
10026
+ }
9297
10027
  const result = errorResult(error, format);
9298
- if (delivered === void 0) return result;
9299
10028
  try {
9300
10029
  return {
9301
10030
  ...result,
9302
- stdout: await stdoutOf(delivered, format, spill)
10031
+ stdout: await stdoutOf(outcome, format, spill)
9303
10032
  };
9304
10033
  } catch (spillError) {
9305
10034
  const both = new AggregateError([...errorsOf(error), spillError]);
@@ -9316,7 +10045,8 @@ function textHelp(node, root, cli, style) {
9316
10045
  function helpOf(node, root, cli, style, json) {
9317
10046
  if (!json) return textHelp(node, root, cli, style);
9318
10047
  if (node === root) return cliHelpJson(cli, node);
9319
- return node.kind === "command" ? commandHelpJson(node) : groupHelpJson(node);
10048
+ const table = errorTableOf(cli);
10049
+ return node.kind === "command" ? commandHelpJson(node, table) : groupHelpJson(node, table);
9320
10050
  }
9321
10051
  function helpResult(node, root, cli, { style, json }) {
9322
10052
  return {
@@ -9327,7 +10057,7 @@ function helpResult(node, root, cli, { style, json }) {
9327
10057
  }
9328
10058
  function versionResult(cli, packageRoot) {
9329
10059
  return {
9330
- stdout: `${(cli.build === void 0 ? [versionLine(cli)] : [versionLine(cli), `changelog ${join(packageRoot, "CHANGELOG.md")}`]).join("\n")}\n`,
10060
+ stdout: `${(buildStamp() === void 0 ? [versionLine(cli)] : [versionLine(cli), `changelog ${join(packageRoot, "CHANGELOG.md")}`]).join("\n")}\n`,
9331
10061
  stderr: "",
9332
10062
  exitCode: SUCCESS_EXIT_CODE
9333
10063
  };
@@ -9347,509 +10077,795 @@ function packageRootOf(bin, cwd) {
9347
10077
  }
9348
10078
  }
9349
10079
  //#endregion
9350
- //#region ../agent-cli/dist/suggest.js
9351
- const MAX_DISTANCE = 3;
9352
- const MIN_SIMILARITY = .4;
9353
- function distance(a, b) {
9354
- const width = b.length + 1;
9355
- const d = Array.from({ length: (a.length + 1) * width }, (_, k) => {
9356
- const i = Math.floor(k / width);
9357
- const j = k % width;
9358
- return i === 0 || j === 0 ? i + j : 0;
9359
- });
9360
- const at = (i, j) => d[i * width + j] ?? 0;
9361
- for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
9362
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
9363
- const edit = Math.min(at(i - 1, j) + 1, at(i, j - 1) + 1, at(i - 1, j - 1) + cost);
9364
- const transposed = i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1];
9365
- d[i * width + j] = transposed ? Math.min(edit, at(i - 2, j - 2) + 1) : edit;
9366
- }
9367
- return at(a.length, b.length);
9368
- }
9369
- function isClose(word, candidate) {
9370
- const length = Math.max(word.length, candidate.length);
9371
- const edits = distance(word, candidate);
9372
- return candidate.length > 1 && edits < MAX_DISTANCE && (length - edits) / length > MIN_SIMILARITY;
9373
- }
9374
- /** The names closest to `word`, sorted, several when they tie, or none when nothing is close. */
9375
- function closestNames(word, names) {
9376
- const close = [...new Set(names)].filter((candidate) => isClose(word, candidate));
9377
- const best = Math.min(...close.map((candidate) => distance(word, candidate)));
9378
- return close.filter((candidate) => distance(word, candidate) === best).sort((a, b) => a.localeCompare(b));
9379
- }
9380
- /** The long flags closest to `flag`, sorted, or none for a short flag or when nothing is close. */
9381
- function closestFlags(flag, flags) {
9382
- if (!flag.startsWith("--")) return [];
9383
- const names = flags.map((candidate) => candidate.slice(2));
9384
- return closestNames(flag.slice(2), names).map((name) => `--${name}`);
9385
- }
9386
- /** The one long flag closest to `flag`, or none when nothing is close or several tie. */
9387
- function closestFlag(flag, flags) {
9388
- const closest = closestFlags(flag, flags);
9389
- return closest.length === 1 ? closest[0] : void 0;
9390
- }
9391
- //#endregion
9392
- //#region ../agent-cli/dist/route.js
9393
- const GROUP_FLAGS = [
9394
- HELP_FLAG,
9395
- VERSION_FLAG,
9396
- JSON_FLAG.flag
9397
- ];
9398
- function beforeEnd(words) {
9399
- const end = words.indexOf("--");
9400
- return end === -1 ? words : words.slice(0, end);
9401
- }
9402
- function wantsHelp(words) {
9403
- return beforeEnd(words).some((word) => word === "--help" || word === "-h");
9404
- }
9405
- function childOf(group, name) {
9406
- return Object.hasOwn(group.commands, name) ? group.commands[name] : void 0;
9407
- }
9408
- function isFlagLike(word) {
9409
- return word.length > 1 && word[0] === "-";
9410
- }
9411
- function walk(node, words, json, invocation) {
9412
- const help = {
9413
- kind: "help",
9414
- node,
9415
- json
9416
- };
9417
- if (node.kind === "command") return wantsHelp(words) ? help : {
9418
- kind: "command",
9419
- spec: node,
9420
- words,
9421
- json
9422
- };
9423
- const [first, ...rest] = words;
9424
- if (first === void 0) return help;
9425
- const child = childOf(node, first);
9426
- if (child !== void 0) return walk(child, rest, json, invocation);
9427
- if (first === "--") {
9428
- const [name, ...after] = rest;
9429
- if (name === void 0) return help;
9430
- const named = childOf(node, name);
9431
- if (named !== void 0) return walk(named, ["--", ...after], json, invocation);
9432
- return unknownCommand(node, name, json);
9433
- }
9434
- if (wantsHelp(words)) return help;
9435
- if (isFlagLike(first)) {
9436
- const suggestions = closestFlags(first, GROUP_FLAGS);
9437
- return {
9438
- kind: "usage error",
9439
- error: unknownOptionError(node.commandLine, invocation, first, suggestions),
9440
- node,
9441
- json
9442
- };
9443
- }
9444
- return unknownCommand(node, first, json);
9445
- }
9446
- function unknownCommand(group, name, json) {
9447
- const suggestions = closestNames(name, Object.keys(visibleCommands(group)));
9448
- return {
9449
- kind: "usage error",
9450
- error: unknownCommandError(group.commandLine, name, suggestions),
9451
- node: group,
9452
- json
10080
+ //#region ../agent-cli/dist/root-spec.js
10081
+ /** The CLI's Commands as routing and help see them, the Built-in Commands included. */
10082
+ function rootSpec(cli, launch) {
10083
+ const names = {
10084
+ name: cli.name,
10085
+ runAs: runAsOf(cli)
9453
10086
  };
10087
+ return cli.commands === void 0 ? {
10088
+ ...commandSpec(names.runAs, cli.command, {
10089
+ path: [],
10090
+ builtIn: false,
10091
+ names
10092
+ }),
10093
+ description: cli.description
10094
+ } : groupSpec(names, cli.commands, builtInCommands(cli, launch));
9454
10095
  }
9455
- /** Walks the caller's command line through the Commands to the one it names, or to a stop. */
9456
- function route(root, invocation) {
9457
- const { argv } = invocation;
9458
- const options = beforeEnd(argv);
9459
- const json = options.includes(JSON_FLAG.flag);
9460
- if (options.includes("--version")) return {
9461
- kind: "version",
9462
- json
9463
- };
9464
- return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
10096
+ //#endregion
10097
+ //#region ../agent-cli/dist/command-errors.js
10098
+ /**
10099
+ * `error`, or a bug naming each Error table code in it that the Command does not declare in its
10100
+ * `errors`. Runtime codes are never in the table, and a built-in's failures, `doctor` rows'
10101
+ * included, belong to no declaration, so both pass.
10102
+ */
10103
+ function checkedCodes(error, { command, path, builtIn }, cli) {
10104
+ if (builtIn) return error;
10105
+ const table = errorTableOf(cli);
10106
+ const declared = new Set(command.errors ?? []);
10107
+ const undeclared = errorsOf(error).flatMap((leaf) => leaf instanceof CliError && Object.hasOwn(table, leaf.code) && !declared.has(leaf.code) ? [leaf.code] : []);
10108
+ if (undeclared.length === 0) return error;
10109
+ const label = commandLabel(path, cli.name).join(" ");
10110
+ const codes = [...new Set(undeclared)].join(", ");
10111
+ return new Error(`\`${label}\` threw ${codes}, which its errors do not declare`, { cause: error });
9465
10112
  }
9466
10113
  //#endregion
9467
- //#region ../agent-cli/dist/execute.js
9468
- function outcomeOf(value, render) {
9469
- return {
9470
- value,
9471
- render: (style) => render(value, style)
9472
- };
10114
+ //#region ../agent-cli/dist/context/operator.js
10115
+ const NEEDS_OPERATOR = "NEEDS_OPERATOR";
10116
+ /** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
10117
+ function checkOperator({ operator, commandLine }, env, rerun) {
10118
+ if (!operator || !isAgent(env)) return;
10119
+ throw new CliError({
10120
+ code: NEEDS_OPERATOR,
10121
+ message: `\`${commandLine}\` must be run by a person at a terminal, not a Coding agent`,
10122
+ hint: "ask the operator to type ! then the Next step",
10123
+ next: [rerun]
10124
+ });
9473
10125
  }
9474
- /** Delivered data as the Command prints it, which is the data its `run` or `apply` returns. */
9475
- function deliveredOutcome(command, data) {
9476
- return outcomeOf(data, command.render);
10126
+ //#endregion
10127
+ //#region ../agent-cli/dist/context/spill.js
10128
+ const STDOUT_BUDGET_CHARS = 3e4;
10129
+ const SPILL_RETENTION_MS = 6048e5;
10130
+ const PREVIEW_CHARS = 2e3;
10131
+ const SPILL_FILE = /^\d{4}-\d\d-\d\dT\d\d-\d\d-\d\d\.\d{3}Z-.+\.(json|txt)$/;
10132
+ function spillName(now, label, json) {
10133
+ return `${new Date(now).toISOString().replaceAll(":", "-")}-${label.join("-")}.${json ? "json" : "txt"}`;
9477
10134
  }
9478
- function dryRunOutcome(command, plan) {
9479
- const renderPlan = (style) => command.renderPlan(plan, style);
9480
- if (command.outputs === void 0 && command.cost === void 0) return {
9481
- value: plan,
9482
- render: renderPlan
9483
- };
9484
- const outputs = command.outputs?.(plan);
9485
- const cost = command.cost?.(plan);
9486
- return {
9487
- value: {
9488
- plan,
9489
- ...outputs === void 0 ? {} : { outputs },
9490
- ...command.cost === void 0 ? {} : { cost: cost ?? null }
9491
- },
9492
- render: (style) => [
9493
- renderPlan(style),
9494
- ...outputs === void 0 || outputs.length === 0 ? [] : ["outputs:", ...outputs.map((path) => ` ${path}`)],
9495
- ...cost === void 0 ? [] : [`cost: ${cost}`]
9496
- ].join("\n")
9497
- };
10135
+ async function pruneOne(path, cutoff) {
10136
+ try {
10137
+ if ((await stat(path)).mtimeMs < cutoff) await rm(path, { force: true });
10138
+ } catch (error) {
10139
+ if (!isMissingFile(error)) throw error;
10140
+ }
9498
10141
  }
9499
- function serveContextOf(ctx, end) {
9500
- const serveCtx = Object.create(ctx);
9501
- return Object.assign(serveCtx, { end });
10142
+ async function pruneOld(folder, cutoff) {
10143
+ const names = (await readdir(folder)).filter((name) => SPILL_FILE.test(name));
10144
+ await Promise.all(names.map((name) => pruneOne(join(folder, name), cutoff)));
9502
10145
  }
9503
- async function execute(command, input, ctx, { dryRun, confirm, yes, page, invocation, commandLine, started, files, guardOutputs }) {
9504
- await checkTools(command.tools ?? [], ctx.env, ctx.commandLine());
9505
- if (command.rows !== void 0) return outcomeOf(await command.run({
9506
- ...input,
9507
- ...page
9508
- }, ctx), (data, style) => command.render(data, style));
9509
- if (command.effect === "read" || command.effect === "act") return outcomeOf(await command.run(input, ctx), (data, style) => command.render(data, style));
9510
- if (command.effect === "serve") {
9511
- let end = () => {};
9512
- const ended = new Promise((resolve) => {
9513
- end = resolve;
10146
+ async function prepare(folder, cutoff, target) {
10147
+ try {
10148
+ await mkdir(folder, { recursive: true });
10149
+ await pruneOld(folder, cutoff);
10150
+ } catch (error) {
10151
+ throw target.fileError(error, {
10152
+ path: folder,
10153
+ access: "write"
9514
10154
  });
9515
- const outcome = outcomeOf(await command.start(input, serveContextOf(ctx, end)), (value, style) => command.render(value, style));
9516
- await started(outcome);
9517
- await Promise.race([ended, untilAborted(ctx.signal)]);
9518
- return outcome;
9519
10155
  }
9520
- const plan = await command.plan(input, ctx);
9521
- if (dryRun) return dryRunOutcome(command, plan);
9522
- const outputs = command.outputs?.(plan);
9523
- await checkOutputs({
9524
- invocation,
9525
- commandLine,
9526
- force: ctx.force,
9527
- files: ctx.files
9528
- }, outputs);
9529
- checkCost({
9530
- invocation,
9531
- commandLine,
9532
- yes
9533
- }, command.cost?.(plan));
9534
- if (command.effect === "destructive") checkConfirmation({
9535
- invocation,
9536
- commandLine,
9537
- given: confirm
9538
- }, command.confirm(plan));
9539
- if (outputs !== void 0) {
9540
- await probeOutputs({
9541
- files,
9542
- cwd: ctx.cwd,
9543
- fileError: ctx.fileError
9544
- }, outputs);
9545
- if (!ctx.force) guardOutputs(outputs, ctx.commandLine({ add: [FORCE_FLAG.flag] }));
10156
+ }
10157
+ function tempRoot(env) {
10158
+ const { TMPDIR } = env;
10159
+ return TMPDIR === void 0 || TMPDIR === "" ? tmpdir() : TMPDIR;
10160
+ }
10161
+ function spillTo(target) {
10162
+ return async (printed, json) => {
10163
+ if (printed.length <= STDOUT_BUDGET_CHARS) return printed;
10164
+ const now = target.clock.now();
10165
+ const folder = join(tempRoot(target.env), target.cliName);
10166
+ await prepare(folder, now - SPILL_RETENTION_MS, target);
10167
+ const file = join(folder, spillName(now, target.label, json));
10168
+ await target.files.write(file, printed);
10169
+ const bytes = Buffer.byteLength(printed);
10170
+ const preview = printed.slice(0, PREVIEW_CHARS);
10171
+ return json ? `${JSON.stringify({
10172
+ file,
10173
+ bytes,
10174
+ preview
10175
+ })}\n` : `file: ${file}\nbytes: ${bytes}\npreview:\n${preview}\n`;
10176
+ };
10177
+ }
10178
+ //#endregion
10179
+ //#region ../../node_modules/.pnpm/@toon-format+toon@4.4.0/node_modules/@toon-format/toon/dist/index.mjs
10180
+ const NULL_LITERAL = "null";
10181
+ const DELIMITERS = {
10182
+ comma: ",",
10183
+ tab: " ",
10184
+ pipe: "|"
10185
+ };
10186
+ const DEFAULT_DELIMITER = DELIMITERS.comma;
10187
+ /**
10188
+ * Escapes special characters in a string for encoding.
10189
+ *
10190
+ * @remarks
10191
+ * Control characters outside `\n`, `\r`, `\t`, `\\`, and `"` are emitted as `\uXXXX`.
10192
+ */
10193
+ function escapeString(value) {
10194
+ return value.replace(/\\/g, `\\\\`).replace(/"/g, `\\"`).replace(/\n/g, `\\n`).replace(/\r/g, `\\r`).replace(/\t/g, `\\t`).replace(/[\u0000-\u001F]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
10195
+ }
10196
+ function isBooleanOrNullLiteral(token) {
10197
+ return token === "true" || token === "false" || token === "null";
10198
+ }
10199
+ /**
10200
+ * Assigns an own data property without invoking inherited accessors.
10201
+ *
10202
+ * @remarks
10203
+ * Plain assignment of `__proto__` would hit the `Object.prototype` setter and
10204
+ * corrupt the prototype chain; `defineProperty` avoids that but is markedly
10205
+ * slower, so every other key takes plain assignment.
10206
+ */
10207
+ function setOwnProperty(target, key, value) {
10208
+ if (key === "__proto__") {
10209
+ Object.defineProperty(target, key, {
10210
+ value,
10211
+ enumerable: true,
10212
+ writable: true,
10213
+ configurable: true
10214
+ });
10215
+ return;
9546
10216
  }
9547
- return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
10217
+ target[key] = value;
9548
10218
  }
9549
- const manifestSchema = object({ bugs: union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0) });
9550
- const NO_MANIFEST = {};
9551
- function manifestAt(packageRoot) {
9552
- let manifest;
9553
- try {
9554
- manifest = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
9555
- } catch {
9556
- return NO_MANIFEST;
10219
+ const COMMENT_LINE_PATTERN = new RegExp(`(?:^?|\\n) *#`);
10220
+ /**
10221
+ * Pre-formatted string that the encoder emits verbatim at a primitive value
10222
+ * position, bypassing quoting, escaping, and number/keyword detection.
10223
+ *
10224
+ * Returned from a replacer for an object or array value, it is ignored and
10225
+ * the container is encoded normally.
10226
+ */
10227
+ var RawString = class {
10228
+ constructor(value) {
10229
+ if (COMMENT_LINE_PATTERN.test(value)) throw new TypeError(`Raw string must not contain a line starting with "#": ${JSON.stringify(value)}`);
10230
+ this.value = value;
10231
+ }
10232
+ };
10233
+ function isRawString(value) {
10234
+ return value instanceof RawString;
10235
+ }
10236
+ const SURROGATE_PATTERN = /[\uD800-\uDFFF]/;
10237
+ function normalizeValue(value) {
10238
+ if (value === null) return null;
10239
+ if (isRawString(value)) return value;
10240
+ if (typeof value === "object" && value !== null && "toJSON" in value && typeof value.toJSON === "function") {
10241
+ const next = value.toJSON();
10242
+ if (next !== value) return normalizeValue(next);
10243
+ }
10244
+ if (typeof value === "string") {
10245
+ assertNoLoneSurrogate(value, "string value");
10246
+ return value;
10247
+ }
10248
+ if (typeof value === "boolean") return value;
10249
+ if (typeof value === "number") {
10250
+ if (Object.is(value, -0)) return 0;
10251
+ if (!Number.isFinite(value)) return null;
10252
+ return value;
10253
+ }
10254
+ if (typeof value === "bigint") {
10255
+ if (value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER) return Number(value);
10256
+ return value.toString();
10257
+ }
10258
+ if (value instanceof Date) return value.toISOString();
10259
+ if (Array.isArray(value)) return Array.from(value, normalizeValue);
10260
+ if (value instanceof Set) return Array.from(value).map(normalizeValue);
10261
+ if (value instanceof Map) return Object.fromEntries(Array.from(value, ([k, v]) => [String(k), normalizeValue(v)]));
10262
+ if (isPlainObject(value)) {
10263
+ const encodedValues = {};
10264
+ for (const key in value) if (Object.hasOwn(value, key)) {
10265
+ assertNoLoneSurrogate(key, "object key");
10266
+ setOwnProperty(encodedValues, key, normalizeValue(value[key]));
10267
+ }
10268
+ return encodedValues;
10269
+ }
10270
+ return null;
10271
+ }
10272
+ function assertNoLoneSurrogate(value, context) {
10273
+ if (!SURROGATE_PATTERN.test(value)) return;
10274
+ for (let index = 0; index < value.length; index++) {
10275
+ const code = value.charCodeAt(index);
10276
+ if (code < 55296 || code > 57343) continue;
10277
+ const isHighSurrogate = code <= 56319;
10278
+ const next = value.charCodeAt(index + 1);
10279
+ if (isHighSurrogate && next >= 56320 && next <= 57343) {
10280
+ index++;
10281
+ continue;
10282
+ }
10283
+ throw new TypeError(`Cannot encode ${context} containing an unpaired surrogate U+${code.toString(16).toUpperCase()} at index ${index}`);
9557
10284
  }
9558
- const parsed = manifestSchema.safeParse(manifest);
9559
- return parsed.success ? parsed.data : NO_MANIFEST;
9560
10285
  }
9561
- //#endregion
9562
- //#region ../agent-cli/dist/parse-input.js
9563
- const NO_INPUT = object({});
9564
- const WHITESPACE = /\s/;
9565
- function knownFlags(spec) {
9566
- return [...spec.options.map((field) => ({
9567
- flag: field.label,
9568
- takes: field.type === "boolean" ? "nothing" : "optional value"
9569
- })), ...spec.standardFlags.map(({ flag, takes }) => ({
9570
- flag,
9571
- takes
9572
- }))];
10286
+ function isJsonPrimitive(value) {
10287
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
9573
10288
  }
9574
- function candidateFlags(known) {
9575
- return [
9576
- ...known.map(({ flag }) => flag),
9577
- VERSION_FLAG,
9578
- HELP_FLAG
9579
- ];
10289
+ function isEncodablePrimitive(value) {
10290
+ return isJsonPrimitive(value) || isRawString(value);
9580
10291
  }
9581
- function unknownOptionsOf(words, candidates) {
9582
- return words.map((word) => {
9583
- const flag = word.split("=")[0] ?? word;
9584
- return {
9585
- flag,
9586
- suggestion: closestFlag(flag, candidates)
9587
- };
9588
- });
10292
+ function isJsonArray(value) {
10293
+ return Array.isArray(value);
9589
10294
  }
9590
- function optionValue(field, all) {
9591
- if (field.variadic) return variadicValue(field, (all ?? []).filter((value) => value !== true));
9592
- const raw = all?.[all.length - 1];
9593
- return field.type === "boolean" && raw === true && field.label === `--no-${field.name}` ? false : raw;
10295
+ function isJsonObject(value) {
10296
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !isRawString(value);
9594
10297
  }
9595
- function converted(field, raw) {
9596
- if (Array.isArray(raw)) return raw.map((element) => converted(field, element));
9597
- if (field.type !== "number" || typeof raw !== "string" || raw.trim() === "") return raw;
9598
- const number = Number(raw);
9599
- return Number.isNaN(number) ? raw : number;
10298
+ function isEmptyObject(value) {
10299
+ return Object.keys(value).length === 0;
9600
10300
  }
9601
- function variadicValue(field, values) {
9602
- return values.length === 0 && !field.required ? void 0 : values;
10301
+ function isPlainObject(value) {
10302
+ if (value === null || typeof value !== "object") return false;
10303
+ const prototype = Object.getPrototypeOf(value);
10304
+ return prototype === null || prototype === Object.prototype;
9603
10305
  }
9604
- function parse(schema, values) {
9605
- const input = Object.fromEntries([...values].filter(([, raw]) => raw !== void 0).map(([field, raw]) => [field.key, converted(field, raw)]));
9606
- return schema.safeParse(input);
10306
+ function isArrayOfPrimitives(value) {
10307
+ return value.length === 0 || value.every((item) => isEncodablePrimitive(item));
9607
10308
  }
9608
- function issueLines(result, values, { howToPass, arrayIssueLine, reported = [], fromStdin }) {
9609
- if (result.success) return [];
9610
- return result.error.issues.flatMap((issue) => {
9611
- const field = [...values.keys()].find((candidate) => candidate.key === issue.path[0]);
9612
- if (field === void 0) return [` ${issue.message}`];
9613
- if (reported.includes(field)) return [];
9614
- const raw = values.get(field);
9615
- if (raw === void 0) return [` ${field.label}: required, ${howToPass(field)}`];
9616
- if (field === fromStdin) return [` ${field.label} from stdin: ${issue.message}`];
9617
- return Array.isArray(raw) ? [arrayIssueLine(field, raw, issue)] : [` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`];
9618
- });
10309
+ function isArrayOfArrays(value) {
10310
+ return value.length === 0 || value.every((item) => isJsonArray(item));
9619
10311
  }
9620
- function flagValuesIssueLine(field, raw, issue) {
9621
- const [, element] = issue.path;
9622
- if (typeof element === "number") return ` ${field.label} ${JSON.stringify(raw[element])}: ${issue.message}`;
9623
- return raw.length === 0 ? ` ${field.label}: ${issue.message}, pass ${field.usage}` : ` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`;
10312
+ function isArrayOfObjects(value) {
10313
+ return value.length === 0 || value.every((item) => isJsonObject(item));
9624
10314
  }
9625
- function operandsIssueLine(field, raw, issue, first) {
9626
- const [, element] = issue.path;
9627
- if (typeof element === "number") return ` ${field.label} ${JSON.stringify(raw[element])} (argument ${first + element}): ${issue.message}`;
9628
- return raw.length === 0 ? ` ${field.label}: ${issue.message}, pass them as arguments from ${first}` : ` ${field.label} ${JSON.stringify(raw)}: ${issue.message}`;
10315
+ const NUMERIC_LIKE_PATTERN = /^[+-]?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i;
10316
+ /** Narrows an arbitrary delimiter option, shared by the library and the CLI so both report it alike. */
10317
+ function assertValidDelimiter(delimiter) {
10318
+ if (!Object.values(DELIMITERS).includes(delimiter)) throw new TypeError(`Invalid delimiter ${JSON.stringify(delimiter)}. Valid delimiters are: comma (,), tab (\\t), pipe (|)`);
9629
10319
  }
9630
- function unknownLine({ flag, suggestion }) {
9631
- return ` unknown option '${flag}'${suggestion === void 0 ? "" : ` (did you mean ${suggestion}?)`}`;
10320
+ /**
10321
+ * Checks if a key can be used without quotes.
10322
+ *
10323
+ * @remarks
10324
+ * Valid unquoted keys must start with a letter or underscore,
10325
+ * followed by letters, digits, underscores, or dots.
10326
+ */
10327
+ function isValidUnquotedKey(key) {
10328
+ return /^[A-Z_][\w.]*$/i.test(key);
10329
+ }
10330
+ /** Determines if a string value can be safely encoded without quotes. */
10331
+ function isSafeUnquoted(value, delimiter = DEFAULT_DELIMITER) {
10332
+ if (!value) return false;
10333
+ if (/^[ \t]|[ \t]$/.test(value)) return false;
10334
+ if (isBooleanOrNullLiteral(value) || isNumericLike(value)) return false;
10335
+ if (value.includes(":")) return false;
10336
+ if (value.includes("\"") || value.includes("\\")) return false;
10337
+ if (/[[\]{}]/.test(value)) return false;
10338
+ if (/[\u0000-\u001F]/.test(value)) return false;
10339
+ if (value.includes(delimiter)) return false;
10340
+ if (value.startsWith("-")) return false;
10341
+ if (value.startsWith("#")) return false;
10342
+ return true;
9632
10343
  }
9633
- function invalidInput(commandLine, invocation, unknown, otherLines) {
9634
- const renames = new Map(unknown.flatMap(({ flag, suggestion }) => suggestion === void 0 ? [] : [[flag, suggestion]]));
9635
- const correctable = otherLines.length === 0 && renames.size === unknown.length;
9636
- return new UsageError({
9637
- code: INVALID_INPUT,
9638
- message: [
9639
- `invalid input for \`${commandLine}\``,
9640
- ...unknown.map(unknownLine),
9641
- ...otherLines
9642
- ].join("\n"),
9643
- hint: correctable ? `did you mean \`${commandLineRenaming(invocation, renames)}\`?` : `run \`${commandLine} --help\` to see the expected input`
9644
- });
10344
+ function isNumericLike(value) {
10345
+ return NUMERIC_LIKE_PATTERN.test(value);
10346
+ }
10347
+ function encodePrimitive(value, delimiter) {
10348
+ if (isRawString(value)) return value.value;
10349
+ if (value === null) return NULL_LITERAL;
10350
+ if (typeof value === "boolean") return String(value);
10351
+ if (typeof value === "number") return String(value);
10352
+ return encodeStringLiteral(value, delimiter);
10353
+ }
10354
+ function encodeStringLiteral(value, delimiter = DEFAULT_DELIMITER) {
10355
+ if (isSafeUnquoted(value, delimiter)) return value;
10356
+ return `"${escapeString(value)}"`;
10357
+ }
10358
+ function encodeKey(key) {
10359
+ if (isValidUnquotedKey(key)) return key;
10360
+ return `"${escapeString(key)}"`;
10361
+ }
10362
+ function encodeAndJoinPrimitives(values, delimiter = DEFAULT_DELIMITER) {
10363
+ return values.map((v) => encodePrimitive(v, delimiter)).join(delimiter);
10364
+ }
10365
+ function formatHeader(length, options) {
10366
+ const key = options?.key;
10367
+ const fields = options?.fields;
10368
+ const delimiter = options?.delimiter ?? ",";
10369
+ let header = "";
10370
+ if (key != null) header += encodeKey(key);
10371
+ header += `[${length}${options?.keyed ? ":" : ""}${delimiter !== DEFAULT_DELIMITER ? delimiter : ""}]`;
10372
+ if (fields) header += `{${formatFieldSegment(fields, delimiter)}}`;
10373
+ header += ":";
10374
+ return header;
10375
+ }
10376
+ function formatFieldSegment(fields, delimiter) {
10377
+ return fields.map((field) => encodeKey(field.name) + (field.children ? `{${formatFieldSegment(field.children, delimiter)}}` : "")).join(delimiter);
10378
+ }
10379
+ /** Classifies rows into a tabular field list, or undefined when they are not uniformly tabular. */
10380
+ function extractTabularFields(rows) {
10381
+ if (rows.length === 0) return;
10382
+ const firstKeys = Object.keys(rows[0]);
10383
+ if (firstKeys.length === 0) return;
10384
+ for (const row of rows) {
10385
+ if (Object.keys(row).length !== firstKeys.length) return;
10386
+ for (const key of firstKeys) if (!Object.hasOwn(row, key)) return;
10387
+ }
10388
+ const fieldNodes = [];
10389
+ for (const key of firstKeys) {
10390
+ const fieldNode = classifyColumn(key, rows.map((row) => row[key]));
10391
+ if (!fieldNode) return;
10392
+ fieldNodes.push(fieldNode);
10393
+ }
10394
+ return fieldNodes;
10395
+ }
10396
+ /** Classifies an object's values as a keyed tabular field list (>=2 uniform non-empty object entries), or undefined. */
10397
+ function extractKeyedTabularFields(value) {
10398
+ const entryValues = Object.values(value);
10399
+ if (entryValues.length < 2) return;
10400
+ if (!entryValues.every((entryValue) => isJsonObject(entryValue) && !isEmptyObject(entryValue))) return;
10401
+ return extractTabularFields(entryValues);
10402
+ }
10403
+ /** Reads one row's leaf cells in the field order `extractTabularFields` produced. */
10404
+ function collectRowLeaves(row, fields) {
10405
+ const leaves = [];
10406
+ collectLeafValues(row, fields, leaves);
10407
+ return leaves;
10408
+ }
10409
+ function classifyColumn(name, values) {
10410
+ if (values.every((value) => isEncodablePrimitive(value))) return { name };
10411
+ if (!values.every((value) => isJsonObject(value) && !isEmptyObject(value))) return;
10412
+ const children = extractTabularFields(values);
10413
+ if (!children) return;
10414
+ return {
10415
+ name,
10416
+ children
10417
+ };
9645
10418
  }
9646
- function stdinFieldOf(spec, operands) {
9647
- const index = spec.args.findIndex((field) => field.stdin);
9648
- return operands[index]?.value === "-" ? spec.args[index] : void 0;
10419
+ function collectLeafValues(row, fields, leaves) {
10420
+ for (const field of fields) {
10421
+ const value = row[field.name];
10422
+ if (field.children) collectLeafValues(value, field.children, leaves);
10423
+ else leaves.push(value);
10424
+ }
9649
10425
  }
9650
- /** The name of the routed Command's arg given `-`, which takes its text from stdin. */
9651
- function stdinArgOf({ spec, words }) {
9652
- return stdinFieldOf(spec, tokenize(words, knownFlags(spec)).operands)?.name;
10426
+ function* encodeJsonValue(value, options, depth) {
10427
+ if (isEncodablePrimitive(value)) {
10428
+ const encodedPrimitive = typeof value === "string" && value.startsWith("") ? `"${escapeString(value)}"` : encodePrimitive(value, options.delimiter);
10429
+ if (encodedPrimitive !== "") yield encodedPrimitive;
10430
+ return;
10431
+ }
10432
+ if (isJsonArray(value)) yield* encodeArrayLines(void 0, value, depth, options);
10433
+ else if (isJsonObject(value)) {
10434
+ const keyedFields = extractKeyedTabularFields(value);
10435
+ if (keyedFields) {
10436
+ yield* encodeKeyedObjectLines(void 0, value, keyedFields, depth, options);
10437
+ return;
10438
+ }
10439
+ yield* encodeObjectLines(value, depth, options);
10440
+ }
9653
10441
  }
9654
- async function stdinTextOf(spec, operands, read) {
9655
- const field = stdinFieldOf(spec, operands);
9656
- return field === void 0 ? void 0 : {
9657
- field,
9658
- text: await read(field)
9659
- };
10442
+ function* encodeObjectLines(value, depth, options) {
10443
+ for (const [key, val] of Object.entries(value)) yield* encodeKeyValuePairLines(key, val, depth, options);
9660
10444
  }
9661
- async function parseInput(spec, words, invocation, readStdin) {
9662
- const { command, args: argFields, options: optionFields } = spec;
9663
- const pageFields = spec.list?.pageFields ?? [];
9664
- const known = knownFlags(spec);
9665
- const { operands, unknownOptions, values, occurrences } = tokenize(words, known);
9666
- const stdin = await stdinTextOf(spec, operands, readStdin);
9667
- if (values.get(CONFIRM_FLAG.flag) === true) throw invalidInputError(spec.commandLine, `option '${CONFIRM_FLAG.usage}' argument missing`);
9668
- const missing = [...optionFields, ...pageFields].filter((field) => field.type !== "boolean" && (field.variadic ? occurrences.get(field.label)?.includes(true) : values.get(field.label) === true));
9669
- const argValue = (field, index) => {
9670
- if (field === stdin?.field) return stdin.text;
9671
- if (!field.variadic) return operands[index]?.value;
9672
- return variadicValue(field, operands.slice(index).filter((operand) => !operand.afterUnknownOption).map((operand) => operand.value));
9673
- };
9674
- const argValues = new Map(argFields.map((field, index) => [field, argValue(field, index)]));
9675
- const extraOperands = argFields.some((field) => field.variadic) ? [] : operands.slice(argFields.length);
9676
- const valuesOf = (fields) => new Map(fields.map((field) => [field, missing.includes(field) ? void 0 : optionValue(field, occurrences.get(field.label))]));
9677
- const flagWording = {
9678
- howToPass: (field) => `pass ${field.usage}`,
9679
- arrayIssueLine: flagValuesIssueLine,
9680
- reported: missing
9681
- };
9682
- const optionValues = valuesOf(optionFields);
9683
- const pageValues = valuesOf(pageFields);
9684
- const args = parse(command.args ?? NO_INPUT, argValues);
9685
- const options = parse(command.options ?? NO_INPUT, optionValues);
9686
- const page = parse(PAGE_INPUT, pageValues);
9687
- const unknown = unknownOptionsOf(unknownOptions, candidateFlags(known));
9688
- const otherLines = [
9689
- ...missing.map((field) => ` ${field.label}: missing value, pass ${field.usage}`),
9690
- ...extraOperands.filter((operand) => !operand.afterUnknownOption).map((operand) => ` unexpected argument ${JSON.stringify(operand.value)}`),
9691
- ...issueLines(args, argValues, {
9692
- howToPass: (field) => `pass it as argument ${argFields.indexOf(field) + 1}`,
9693
- arrayIssueLine: (field, raw, issue) => operandsIssueLine(field, raw, issue, argFields.indexOf(field) + 1),
9694
- fromStdin: stdin?.field
9695
- }),
9696
- ...issueLines(options, optionValues, flagWording),
9697
- ...issueLines(page, pageValues, flagWording)
9698
- ];
9699
- if (unknown.length === 0 && otherLines.length === 0 && args.success && options.success && page.success) return {
9700
- input: {
9701
- ...args.data,
9702
- ...options.data
9703
- },
9704
- page: page.data,
9705
- values
9706
- };
9707
- const [onlyWord] = unknownOptions;
9708
- if (onlyWord !== void 0 && unknownOptions.length === 1 && WHITESPACE.test(onlyWord) && argFields.some((field) => field.stdin && argValues.get(field) === void 0)) throw stdinTextError(spec.commandLine, invocation, onlyWord);
9709
- const [only] = unknown;
9710
- if (only !== void 0 && unknown.length === 1 && otherLines.length === 0) throw unknownOptionError(spec.commandLine, invocation, only.flag, only.suggestion === void 0 ? [] : [only.suggestion]);
9711
- throw invalidInput(spec.commandLine, invocation, unknown, otherLines);
10445
+ function* encodeKeyValuePairLines(key, value, depth, options) {
10446
+ const encodedKey = encodeKey(key);
10447
+ if (isEncodablePrimitive(value)) yield indentedLine(depth, `${encodedKey}: ${encodePrimitive(value, options.delimiter)}`, options.indentSize);
10448
+ else if (isJsonArray(value)) yield* encodeArrayLines(key, value, depth, options);
10449
+ else if (isJsonObject(value)) {
10450
+ const keyedFields = extractKeyedTabularFields(value);
10451
+ if (keyedFields) {
10452
+ yield* encodeKeyedObjectLines(key, value, keyedFields, depth, options);
10453
+ return;
10454
+ }
10455
+ yield indentedLine(depth, `${encodedKey}:`, options.indentSize);
10456
+ if (!isEmptyObject(value)) yield* encodeObjectLines(value, depth + 1, options);
10457
+ }
10458
+ }
10459
+ function* encodeKeyedObjectLines(key, value, fields, depth, options) {
10460
+ const entries = Object.entries(value);
10461
+ yield indentedLine(depth, formatHeader(entries.length, {
10462
+ key,
10463
+ fields,
10464
+ delimiter: options.delimiter,
10465
+ keyed: true
10466
+ }), options.indentSize);
10467
+ yield* encodeKeyedEntryRowsLines(entries, fields, depth + 1, options);
10468
+ }
10469
+ function* encodeKeyedEntryRowsLines(entries, fields, depth, options) {
10470
+ for (const [entryKey, entryValue] of entries) {
10471
+ const leaves = collectRowLeaves(entryValue, fields);
10472
+ yield indentedLine(depth, `${encodeKey(entryKey)}: ${encodeAndJoinPrimitives(leaves, options.delimiter)}`, options.indentSize);
10473
+ }
10474
+ }
10475
+ function* encodeArrayLines(key, value, depth, options) {
10476
+ if (value.length === 0) {
10477
+ yield indentedLine(depth, key != null ? `${encodeKey(key)}: []` : "[]", options.indentSize);
10478
+ return;
10479
+ }
10480
+ if (isArrayOfPrimitives(value)) {
10481
+ yield indentedLine(depth, encodeInlineArrayLine(value, options.delimiter, key), options.indentSize);
10482
+ return;
10483
+ }
10484
+ if (isArrayOfArrays(value)) {
10485
+ if (value.every((arr) => isArrayOfPrimitives(arr))) {
10486
+ yield* encodeArrayOfArraysAsListItemsLines(key, value, depth, options);
10487
+ return;
10488
+ }
10489
+ }
10490
+ if (isArrayOfObjects(value)) {
10491
+ const fields = extractTabularFields(value);
10492
+ if (fields) yield* encodeArrayOfObjectsAsTabularLines(key, value, fields, depth, options);
10493
+ else yield* encodeMixedArrayAsListItemsLines(key, value, depth, options);
10494
+ return;
10495
+ }
10496
+ yield* encodeMixedArrayAsListItemsLines(key, value, depth, options);
10497
+ }
10498
+ function* encodeArrayOfArraysAsListItemsLines(prefix, values, depth, options) {
10499
+ yield indentedLine(depth, formatHeader(values.length, {
10500
+ key: prefix,
10501
+ delimiter: options.delimiter
10502
+ }), options.indentSize);
10503
+ for (const arr of values) if (isArrayOfPrimitives(arr)) {
10504
+ const arrayLine = encodeInlineArrayLine(arr, options.delimiter);
10505
+ yield indentedListItem(depth + 1, arrayLine, options.indentSize);
10506
+ }
10507
+ }
10508
+ function encodeInlineArrayLine(values, delimiter, prefix) {
10509
+ const header = formatHeader(values.length, {
10510
+ key: prefix,
10511
+ delimiter
10512
+ });
10513
+ const joinedValue = encodeAndJoinPrimitives(values, delimiter);
10514
+ if (values.length === 0) return header;
10515
+ return `${header} ${joinedValue}`;
10516
+ }
10517
+ function* encodeArrayOfObjectsAsTabularLines(prefix, rows, fields, depth, options) {
10518
+ yield indentedLine(depth, formatHeader(rows.length, {
10519
+ key: prefix,
10520
+ fields,
10521
+ delimiter: options.delimiter
10522
+ }), options.indentSize);
10523
+ yield* writeTabularRowsLines(rows, fields, depth + 1, options);
10524
+ }
10525
+ function* writeTabularRowsLines(rows, fields, depth, options) {
10526
+ for (const row of rows) yield indentedLine(depth, encodeAndJoinPrimitives(collectRowLeaves(row, fields), options.delimiter), options.indentSize);
10527
+ }
10528
+ function* encodeMixedArrayAsListItemsLines(prefix, items, depth, options) {
10529
+ yield indentedLine(depth, formatHeader(items.length, {
10530
+ key: prefix,
10531
+ delimiter: options.delimiter
10532
+ }), options.indentSize);
10533
+ for (const item of items) yield* encodeListItemValueLines(item, depth + 1, options);
10534
+ }
10535
+ function* encodeObjectAsListItemLines(obj, depth, options) {
10536
+ if (isEmptyObject(obj)) {
10537
+ yield indentedLine(depth, "-", options.indentSize);
10538
+ return;
10539
+ }
10540
+ const entries = Object.entries(obj);
10541
+ const [firstKey, firstValue] = entries[0];
10542
+ const restEntries = entries.slice(1);
10543
+ if (isJsonArray(firstValue) && isArrayOfObjects(firstValue)) {
10544
+ const fields = extractTabularFields(firstValue);
10545
+ if (fields) {
10546
+ yield indentedListItem(depth, formatHeader(firstValue.length, {
10547
+ key: firstKey,
10548
+ fields,
10549
+ delimiter: options.delimiter
10550
+ }), options.indentSize);
10551
+ yield* writeTabularRowsLines(firstValue, fields, depth + 2, options);
10552
+ if (restEntries.length > 0) yield* encodeObjectLines(Object.fromEntries(restEntries), depth + 1, options);
10553
+ return;
10554
+ }
10555
+ }
10556
+ if (isJsonObject(firstValue)) {
10557
+ const keyedFields = extractKeyedTabularFields(firstValue);
10558
+ if (keyedFields) {
10559
+ const keyedEntries = Object.entries(firstValue);
10560
+ yield indentedListItem(depth, formatHeader(keyedEntries.length, {
10561
+ key: firstKey,
10562
+ fields: keyedFields,
10563
+ delimiter: options.delimiter,
10564
+ keyed: true
10565
+ }), options.indentSize);
10566
+ yield* encodeKeyedEntryRowsLines(keyedEntries, keyedFields, depth + 2, options);
10567
+ if (restEntries.length > 0) yield* encodeObjectLines(Object.fromEntries(restEntries), depth + 1, options);
10568
+ return;
10569
+ }
10570
+ }
10571
+ const encodedKey = encodeKey(firstKey);
10572
+ if (isEncodablePrimitive(firstValue)) yield indentedListItem(depth, `${encodedKey}: ${encodePrimitive(firstValue, options.delimiter)}`, options.indentSize);
10573
+ else if (isJsonArray(firstValue)) {
10574
+ if (firstValue.length === 0) yield indentedListItem(depth, `${encodedKey}: []`, options.indentSize);
10575
+ else if (isArrayOfPrimitives(firstValue)) yield indentedListItem(depth, `${encodedKey}${encodeInlineArrayLine(firstValue, options.delimiter)}`, options.indentSize);
10576
+ else {
10577
+ yield indentedListItem(depth, `${encodedKey}${formatHeader(firstValue.length, { delimiter: options.delimiter })}`, options.indentSize);
10578
+ for (const item of firstValue) yield* encodeListItemValueLines(item, depth + 2, options);
10579
+ }
10580
+ } else if (isJsonObject(firstValue)) {
10581
+ yield indentedListItem(depth, `${encodedKey}:`, options.indentSize);
10582
+ if (!isEmptyObject(firstValue)) yield* encodeObjectLines(firstValue, depth + 2, options);
10583
+ }
10584
+ if (restEntries.length > 0) yield* encodeObjectLines(Object.fromEntries(restEntries), depth + 1, options);
10585
+ }
10586
+ function* encodeListItemValueLines(value, depth, options) {
10587
+ if (isEncodablePrimitive(value)) yield indentedListItem(depth, encodePrimitive(value, options.delimiter), options.indentSize);
10588
+ else if (isJsonArray(value)) {
10589
+ if (isArrayOfPrimitives(value)) yield indentedListItem(depth, encodeInlineArrayLine(value, options.delimiter), options.indentSize);
10590
+ else {
10591
+ yield indentedListItem(depth, formatHeader(value.length, { delimiter: options.delimiter }), options.indentSize);
10592
+ for (const item of value) yield* encodeListItemValueLines(item, depth + 1, options);
10593
+ }
10594
+ } else if (isJsonObject(value)) yield* encodeObjectAsListItemLines(value, depth, options);
10595
+ }
10596
+ function indentedLine(depth, content, indentSize) {
10597
+ return " ".repeat(indentSize * depth) + content;
10598
+ }
10599
+ function indentedListItem(depth, content, indentSize) {
10600
+ return indentedLine(depth, "- " + content, indentSize);
9712
10601
  }
9713
10602
  /**
9714
- * The validated input and Standard flag values of the routed Command, or one usage error; the arg
9715
- * given `-` takes its text from `readStdin`.
10603
+ * Applies a replacer function to a `JsonValue` and all its descendants.
10604
+ *
10605
+ * The replacer is called for the root (key='', path=[]), every object property
10606
+ * (key = property name), and every array element (key = string index).
9716
10607
  */
9717
- async function parseCommand({ spec, words, json }, invocation, readStdin) {
9718
- const { input, page, values } = await parseInput(spec, words, invocation, readStdin);
9719
- const confirm = values.get(CONFIRM_FLAG.flag);
9720
- return {
9721
- input,
9722
- flags: {
9723
- json,
9724
- dryRun: values.get(DRY_RUN_FLAG.flag) === true,
9725
- confirm: typeof confirm === "string" ? confirm : void 0,
9726
- yes: values.get(YES_FLAG.flag) === true,
9727
- force: values.get(FORCE_FLAG.flag) === true,
9728
- page,
9729
- fields: spec.list === void 0 ? void 0 : selectedFields(values, spec.list.rowFields, spec.commandLine)
9730
- }
9731
- };
10608
+ function applyReplacer(root, replacer) {
10609
+ const replacedRoot = replacer("", root, []);
10610
+ if (replacedRoot === void 0) return transformChildren(root, replacer, []);
10611
+ return transformReplaced(root, replacedRoot, replacer, []);
9732
10612
  }
9733
- //#endregion
9734
- //#region ../agent-cli/dist/secrets.js
9735
- /** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
9736
- function envFileOf(cliName, env) {
9737
- const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
9738
- return join(config, cliName, ".env");
10613
+ /**
10614
+ * Resolves a replacer's (non-`undefined`) return value at a single position.
10615
+ *
10616
+ * A `RawString` only stands in for a primitive: returned for an object or
10617
+ * array value, it is ignored and the original container is traversed normally.
10618
+ */
10619
+ function transformReplaced(original, replaced, replacer, path) {
10620
+ if (isRawString(replaced) && !isEncodablePrimitive(original)) return transformChildren(original, replacer, path);
10621
+ return transformChildren(normalizeValue(replaced), replacer, path);
9739
10622
  }
9740
- function readDotenv(path, fileError) {
9741
- let text;
9742
- try {
9743
- text = readFileSync(path, "utf8");
9744
- } catch (error) {
9745
- if (isMissingFile(error)) return {};
9746
- throw fileError(error, {
9747
- path,
9748
- access: "read"
9749
- });
10623
+ function transformChildren(value, replacer, path) {
10624
+ if (isJsonObject(value)) return transformObject(value, replacer, path);
10625
+ if (isJsonArray(value)) return transformArray(value, replacer, path);
10626
+ return value;
10627
+ }
10628
+ function transformObject(obj, replacer, path) {
10629
+ const result = {};
10630
+ for (const [key, value] of Object.entries(obj)) {
10631
+ const childPath = [...path, key];
10632
+ const replacedValue = replacer(key, value, childPath);
10633
+ if (replacedValue === void 0) continue;
10634
+ setOwnProperty(result, key, transformReplaced(value, replacedValue, replacer, childPath));
9750
10635
  }
9751
- return parseEnv(text);
10636
+ return result;
9752
10637
  }
9753
- /** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
9754
- function secretOf({ envFile, env, fileError }, name) {
9755
- const fromEnv = env[name];
9756
- if (fromEnv) return {
9757
- value: fromEnv,
9758
- origin: { kind: "env" }
10638
+ function transformArray(arr, replacer, path) {
10639
+ const result = [];
10640
+ for (let i = 0; i < arr.length; i++) {
10641
+ const value = arr[i];
10642
+ const childPath = [...path, i];
10643
+ const replacedValue = replacer(String(i), value, childPath);
10644
+ if (replacedValue === void 0) continue;
10645
+ result.push(transformReplaced(value, replacedValue, replacer, childPath));
10646
+ }
10647
+ return result;
10648
+ }
10649
+ /**
10650
+ * Encodes a JavaScript value into TOON format string.
10651
+ *
10652
+ * @example
10653
+ * ```ts
10654
+ * encode({ name: 'Ada', age: 30 })
10655
+ * // name: Ada
10656
+ * // age: 30
10657
+ *
10658
+ * encode({ users: [{ id: 1 }, { id: 2 }] })
10659
+ * // users[2]{id}:
10660
+ * // 1
10661
+ * // 2
10662
+ *
10663
+ * encode({ tags: [] })
10664
+ * // tags: []
10665
+ *
10666
+ * encode(data, { indentSize: 4 })
10667
+ * ```
10668
+ */
10669
+ function encode(input, options) {
10670
+ return Array.from(encodeLines(input, options)).join("\n");
10671
+ }
10672
+ /**
10673
+ * Encodes a JavaScript value into TOON format as a sequence of lines.
10674
+ *
10675
+ * This function yields TOON lines one at a time without building the full string,
10676
+ * making it suitable for streaming large outputs to files, HTTP responses, or process stdout.
10677
+ *
10678
+ * @example
10679
+ * ```ts
10680
+ * // Stream to stdout
10681
+ * for (const line of encodeLines({ name: 'Ada', age: 30 })) {
10682
+ * console.log(line)
10683
+ * }
10684
+ *
10685
+ * // Collect to array
10686
+ * const lines = Array.from(encodeLines(data))
10687
+ *
10688
+ * // Equivalent to encode()
10689
+ * const toonString = Array.from(encodeLines(data, options)).join('\n')
10690
+ * ```
10691
+ */
10692
+ function encodeLines(input, options) {
10693
+ const normalizedValue = normalizeValue(input);
10694
+ const resolvedOptions = resolveOptions(options);
10695
+ return encodeJsonValue(resolvedOptions.replacer ? applyReplacer(normalizedValue, resolvedOptions.replacer) : normalizedValue, resolvedOptions, 0);
10696
+ }
10697
+ function resolveOptions(options) {
10698
+ const delimiter = options?.delimiter ?? DEFAULT_DELIMITER;
10699
+ assertValidDelimiter(delimiter);
10700
+ return {
10701
+ indentSize: options?.indentSize ?? options?.indent ?? 2,
10702
+ delimiter,
10703
+ replacer: options?.replacer
9759
10704
  };
9760
- const fromFile = readDotenv(envFile, fileError)[name];
9761
- return fromFile ? {
9762
- value: fromFile,
9763
- origin: {
9764
- kind: "file",
9765
- path: envFile
9766
- }
9767
- } : void 0;
9768
10705
  }
9769
10706
  //#endregion
9770
- //#region ../agent-cli/dist/side-task.js
9771
- /** How long a run waits for its Side task once its output is written. */
9772
- const SIDE_TASK_WAIT_MS = 1e3;
9773
- const noSideTask = async () => {};
9774
- function waitOver() {
9775
- return new DOMException(`the side task ran past its ${SIDE_TASK_WAIT_MS} ms wait`, "TimeoutError");
10707
+ //#region ../agent-cli/dist/context/confirm.js
10708
+ const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
10709
+ const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
10710
+ function checkConfirmation({ rerun, commandLine, given }, expected) {
10711
+ if (given === expected) return;
10712
+ const advice = {
10713
+ hint: "to confirm, run the Next step",
10714
+ next: [rerun({
10715
+ remove: [CONFIRM_FLAG.flag],
10716
+ add: [CONFIRM_FLAG.flag, expected]
10717
+ })]
10718
+ };
10719
+ if (given === void 0) throw new UsageError({
10720
+ code: CONFIRMATION_REQUIRED,
10721
+ message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
10722
+ ...advice
10723
+ });
10724
+ throw new UsageError({
10725
+ code: CONFIRMATION_MISMATCH,
10726
+ message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
10727
+ ...advice
10728
+ });
9776
10729
  }
9777
- function startSideTask(sideTask, runWith, { clock, failed }) {
9778
- if (sideTask === void 0) return noSideTask;
9779
- const controller = new AbortController();
9780
- const { signal } = controller;
9781
- const running = (async () => sideTask(runWith(signal)))().catch((error) => {
9782
- if (!(signal.aborted && error === signal.reason)) failed(error);
10730
+ //#endregion
10731
+ //#region ../agent-cli/dist/context/cost.js
10732
+ const NOT_CONFIRMED = "NOT_CONFIRMED";
10733
+ /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
10734
+ function checkCost({ rerun, commandLine, yes }, cost) {
10735
+ if (cost === void 0 || yes) return;
10736
+ throw new CliError({
10737
+ code: NOT_CONFIRMED,
10738
+ message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
10739
+ hint: "to approve the cost, run the Next step",
10740
+ next: [rerun({ add: [YES_FLAG.flag] })]
9783
10741
  });
9784
- return async () => {
9785
- const waited = new AbortController();
9786
- const capped = new Promise((resolve) => after(clock, SIDE_TASK_WAIT_MS, waited.signal, () => {
9787
- const over = waitOver();
9788
- failed(over);
9789
- controller.abort(over);
9790
- resolve();
9791
- }));
9792
- await Promise.race([running, capped]);
9793
- waited.abort();
9794
- };
9795
10742
  }
9796
10743
  //#endregion
9797
- //#region ../agent-cli/dist/spill.js
9798
- const STDOUT_BUDGET_CHARS = 3e4;
9799
- const SPILL_RETENTION_MS = 6048e5;
9800
- const PREVIEW_CHARS = 2e3;
9801
- const SPILL_FILE = /^\d{4}-\d\d-\d\dT\d\d-\d\d-\d\d\.\d{3}Z-.+\.(json|txt)$/;
9802
- function spillName(now, commandName, json) {
9803
- return `${new Date(now).toISOString().replaceAll(":", "-")}-${commandName}.${json ? "json" : "txt"}`;
10744
+ //#region ../agent-cli/dist/execute.js
10745
+ const PLAN_INDENT = " ";
10746
+ function outcomeOf(value, render) {
10747
+ return {
10748
+ value,
10749
+ render: (style) => render(value, style)
10750
+ };
9804
10751
  }
9805
- async function pruneOne(path, cutoff) {
9806
- try {
9807
- if ((await stat(path)).mtimeMs < cutoff) await rm(path, { force: true });
9808
- } catch (error) {
9809
- if (!isMissingFile(error)) throw error;
9810
- }
10752
+ /**
10753
+ * The data a Command returned or delivered as it prints it: parsed by its `output` schema when it
10754
+ * declares one, so data the schema rejects fails the run as a bug.
10755
+ */
10756
+ function dataOutcome(command, data, commandLine) {
10757
+ const render = command.render ?? ((value) => value === void 0 ? "" : encode(value));
10758
+ if (command.rows !== void 0 || command.output === void 0) return outcomeOf(data, render);
10759
+ const parsed = command.output.safeParse(data);
10760
+ if (!parsed.success) throw new Error(`\`${commandLine}\` produced data its output schema rejects:\n${prettifyError(parsed.error)}`);
10761
+ return outcomeOf(parsed.data, render);
10762
+ }
10763
+ function indented(text) {
10764
+ return text.split("\n").map((line) => `${PLAN_INDENT}${line}`).join("\n");
10765
+ }
10766
+ function dryRunOutcome(command, plan, commandLine) {
10767
+ const renderPlan = (style) => command.renderPlan === void 0 ? `Would run ${commandLine}:\n${indented(encode(plan))}` : command.renderPlan(plan, style);
10768
+ if (command.outputs === void 0 && command.cost === void 0) return {
10769
+ value: plan,
10770
+ render: renderPlan
10771
+ };
10772
+ const outputs = command.outputs?.(plan);
10773
+ const cost = command.cost?.(plan);
10774
+ return {
10775
+ value: {
10776
+ plan,
10777
+ ...outputs === void 0 ? {} : { outputs },
10778
+ ...command.cost === void 0 ? {} : { cost: cost ?? null }
10779
+ },
10780
+ render: (style) => [
10781
+ renderPlan(style),
10782
+ ...outputs === void 0 || outputs.length === 0 ? [] : ["outputs:", ...outputs.map((path) => ` ${path}`)],
10783
+ ...cost === void 0 ? [] : [`cost: ${cost}`]
10784
+ ].join("\n")
10785
+ };
9811
10786
  }
9812
- async function pruneOld(folder, cutoff) {
9813
- const names = (await readdir(folder)).filter((name) => SPILL_FILE.test(name));
9814
- await Promise.all(names.map((name) => pruneOne(join(folder, name), cutoff)));
10787
+ function serveContextOf(ctx, end) {
10788
+ const serveCtx = Object.create(ctx);
10789
+ return Object.assign(serveCtx, { end });
9815
10790
  }
9816
- async function prepare(folder, cutoff, target) {
9817
- try {
9818
- await mkdir(folder, { recursive: true });
9819
- await pruneOld(folder, cutoff);
9820
- } catch (error) {
9821
- throw target.fileError(error, {
9822
- path: folder,
9823
- access: "write"
10791
+ async function execute(command, input, ctx, { dryRun, confirm, yes, page, commandLine, started, files, guardOutputs }) {
10792
+ await checkTools(command.tools ?? [], ctx.env, ctx.rerun());
10793
+ const { env, envFile, fileError } = ctx;
10794
+ checkSecrets(command.secrets ?? [], {
10795
+ env,
10796
+ envFile,
10797
+ fileError
10798
+ }, ctx.rerun());
10799
+ if (command.rows !== void 0) {
10800
+ const { render } = command;
10801
+ return outcomeOf(await command.run({
10802
+ ...input,
10803
+ ...page
10804
+ }, ctx), (data, style) => render === void 0 ? encode(data.items) : render(data, style));
10805
+ }
10806
+ if (command.effect === "read" || command.effect === "act") return dataOutcome(command, await command.run(input, ctx), commandLine);
10807
+ if (command.effect === "serve") {
10808
+ let end = () => {};
10809
+ const ended = new Promise((resolve) => {
10810
+ end = resolve;
9824
10811
  });
10812
+ const outcome = dataOutcome(command, await command.start(input, serveContextOf(ctx, end)), commandLine);
10813
+ await started(outcome);
10814
+ await Promise.race([ended, untilAborted(ctx.signal)]);
10815
+ return outcome;
9825
10816
  }
10817
+ const plan = await command.plan(input, ctx);
10818
+ if (dryRun) return dryRunOutcome(command, plan, commandLine);
10819
+ const outputs = command.outputs?.(plan);
10820
+ await checkOutputs({
10821
+ rerun: ctx.rerun,
10822
+ commandLine,
10823
+ force: ctx.force,
10824
+ files: ctx.files
10825
+ }, outputs);
10826
+ checkCost({
10827
+ rerun: ctx.rerun,
10828
+ commandLine,
10829
+ yes
10830
+ }, command.cost?.(plan));
10831
+ if (command.effect === "destructive") checkConfirmation({
10832
+ rerun: ctx.rerun,
10833
+ commandLine,
10834
+ given: confirm
10835
+ }, command.confirm(plan));
10836
+ if (outputs !== void 0) {
10837
+ await probeOutputs({
10838
+ files,
10839
+ cwd: ctx.cwd,
10840
+ fileError: ctx.fileError
10841
+ }, outputs);
10842
+ if (!ctx.force) guardOutputs(outputs, ctx.rerun({ add: [FORCE_FLAG.flag] }));
10843
+ }
10844
+ return dataOutcome(command, await command.apply(plan, ctx), commandLine);
9826
10845
  }
9827
- function tempRoot(env) {
9828
- const { TMPDIR } = env;
9829
- return TMPDIR === void 0 || TMPDIR === "" ? tmpdir() : TMPDIR;
9830
- }
9831
- function spillTo(target) {
9832
- return async (printed, json) => {
9833
- if (printed.length <= STDOUT_BUDGET_CHARS) return printed;
9834
- const now = target.clock.now();
9835
- const folder = join(tempRoot(target.env), target.cliName);
9836
- await prepare(folder, now - SPILL_RETENTION_MS, target);
9837
- const file = join(folder, spillName(now, target.commandName, json));
9838
- await target.files.write(file, printed);
9839
- const bytes = Buffer.byteLength(printed);
9840
- const preview = printed.slice(0, PREVIEW_CHARS);
9841
- return json ? `${JSON.stringify({
9842
- file,
9843
- bytes,
9844
- preview
9845
- })}\n` : `file: ${file}\nbytes: ${bytes}\npreview:\n${preview}\n`;
9846
- };
10846
+ const manifestSchema = object({ bugs: union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0) });
10847
+ const NO_MANIFEST = {};
10848
+ function manifestAt(packageRoot) {
10849
+ let manifest;
10850
+ try {
10851
+ manifest = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
10852
+ } catch {
10853
+ return NO_MANIFEST;
10854
+ }
10855
+ const parsed = manifestSchema.safeParse(manifest);
10856
+ return parsed.success ? parsed.data : NO_MANIFEST;
9847
10857
  }
9848
10858
  //#endregion
9849
10859
  //#region ../agent-cli/dist/run-command.js
9850
10860
  function stdinNeverEndedHint(callerLine) {
9851
10861
  return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
9852
10862
  }
10863
+ function runDeadlineAdvice(resumed, pipedLine) {
10864
+ return resumed === void 0 ? { hint: stdinNeverEndedHint(pipedLine) } : {
10865
+ hint: "run the Next step to continue the work",
10866
+ next: [resumed]
10867
+ };
10868
+ }
9853
10869
  function selfOf(name, bin) {
9854
10870
  return bin === void 0 ? [name] : [
9855
10871
  process.execPath,
@@ -9869,7 +10885,7 @@ function portsOf(cli, replacement) {
9869
10885
  return cli.ports ?? (() => ({}));
9870
10886
  }
9871
10887
  function portsBaseOf(scope, signal) {
9872
- const { cli, options, packageRoot, progress, fileError, callerLine, clock, files } = scope;
10888
+ const { cli, options, packageRoot, progress, fileError, rerun, clock, files } = scope;
9873
10889
  const { cwd, env } = options;
9874
10890
  const envFile = envFileOf(cli.name, env);
9875
10891
  return {
@@ -9885,7 +10901,7 @@ function portsBaseOf(scope, signal) {
9885
10901
  envFile,
9886
10902
  fileError,
9887
10903
  version: cli.version,
9888
- build: cli.build,
10904
+ build: buildStamp(),
9889
10905
  self: selfOf(cli.name, options.bin),
9890
10906
  signal,
9891
10907
  clock,
@@ -9896,7 +10912,7 @@ function portsBaseOf(scope, signal) {
9896
10912
  signal
9897
10913
  }, {
9898
10914
  fileError,
9899
- callerLine
10915
+ rerun
9900
10916
  }),
9901
10917
  spawnDetached: contextSpawnDetached(spawnDetachedOf(options), {
9902
10918
  cwd,
@@ -9904,23 +10920,30 @@ function portsBaseOf(scope, signal) {
9904
10920
  signal
9905
10921
  }, {
9906
10922
  fileError,
9907
- callerLine
10923
+ rerun
9908
10924
  })
9909
10925
  };
9910
10926
  }
9911
10927
  function contextOf(portsBase, control, parts) {
9912
- const { ports, project, force, commandLine, deliver, notice } = parts;
10928
+ const { ports, project, force, secrets = [], rerun, deliver, notice } = parts;
10929
+ const declared = new Set(secrets.map(({ name }) => name));
9913
10930
  return {
9914
10931
  ...portsBase,
9915
- deadline: (ms) => control.deadline(ms),
9916
- timeout: (ms) => control.timeout(ms),
10932
+ secret: (name) => {
10933
+ if (!declared.has(name)) throw new Error(`ctx.secret("${name}") names a Secret the Command does not declare`);
10934
+ const secret = portsBase.secret(name);
10935
+ if (secret === void 0) throw new Error(`ctx.secret("${name}") found no value`);
10936
+ return secret;
10937
+ },
10938
+ fail: cliErrorOf,
10939
+ deadline: (ms, options) => control.deadline(ms, options),
9917
10940
  runToEnd: (name, work) => control.runToEnd(name, work),
9918
10941
  get ports() {
9919
10942
  return ports();
9920
10943
  },
9921
10944
  project,
9922
10945
  force,
9923
- commandLine,
10946
+ rerun,
9924
10947
  poll: (check, { intervalMs, signal: stop }) => poll(portsBase.clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
9925
10948
  deliver,
9926
10949
  notice
@@ -9942,10 +10965,14 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9942
10965
  flags: knownFlags(routed.spec),
9943
10966
  stdinArg: stdinArgOf(routed)
9944
10967
  };
9945
- const typedLine = (change) => commandLineOf(invocation, change);
9946
- const rerunLine = typedLine();
10968
+ const rerunOf = (change) => ({
10969
+ kind: "rerun",
10970
+ words: changedArgv(invocation, change),
10971
+ stdinArg: invocation.stdinArg
10972
+ });
10973
+ const rerun = rerunOf();
9947
10974
  const fileError = fileErrorOf({
9948
- callerLine: rerunLine,
10975
+ rerun,
9949
10976
  env,
9950
10977
  platform
9951
10978
  });
@@ -9959,16 +10986,16 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9959
10986
  ...invocation,
9960
10987
  stdinArg: void 0
9961
10988
  });
9962
- let resumeHint;
10989
+ let resumed;
9963
10990
  const control = new RunControl({
9964
10991
  commandLine,
9965
- callerLine: rerunLine,
10992
+ rerun,
9966
10993
  outer: options.signal,
9967
10994
  clock,
9968
10995
  progress,
9969
10996
  runDeadline: command.deadline === void 0 ? void 0 : {
9970
10997
  ms: command.deadline.ms,
9971
- hint: () => resumeHint ?? stdinNeverEndedHint(pipedLine)
10998
+ advice: () => runDeadlineAdvice(resumed, pipedLine)
9972
10999
  }
9973
11000
  });
9974
11001
  const failed = (error, finishSideTask) => {
@@ -9980,7 +11007,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9980
11007
  };
9981
11008
  let parsed;
9982
11009
  try {
9983
- checkOperator(routed.spec, env, typedLine());
11010
+ checkOperator(routed.spec, env, rerun);
9984
11011
  parsed = await parseCommand(routed, invocation, (field) => {
9985
11012
  if (isTerminal(stdin)) progress(readingLine(field.name, platform));
9986
11013
  return readStdin(stdin, control.signal, {
@@ -9992,7 +11019,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
9992
11019
  return failed(error, noSideTask);
9993
11020
  }
9994
11021
  const { input, flags } = parsed;
9995
- resumeHint = command.deadline?.resume(input, typedLine);
11022
+ resumed = command.deadline?.resume(input, rerunOf);
9996
11023
  const outputs = outputsGuard(classifiedFiles(files, {
9997
11024
  cwd,
9998
11025
  fileError
@@ -10003,7 +11030,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10003
11030
  packageRoot,
10004
11031
  progress,
10005
11032
  fileError,
10006
- callerLine: rerunLine,
11033
+ rerun,
10007
11034
  clock,
10008
11035
  files: outputs.files
10009
11036
  };
@@ -10029,7 +11056,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10029
11056
  try {
10030
11057
  project = builtIn || cli.project === void 0 ? void 0 : projectOf(cli.project, cwd, {
10031
11058
  fileError,
10032
- callerLine: rerunLine
11059
+ rerun
10033
11060
  });
10034
11061
  } catch (error) {
10035
11062
  return failed(error, finishSideTask);
@@ -10039,15 +11066,16 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10039
11066
  ports: once(() => ports(portsBase)),
10040
11067
  project,
10041
11068
  force: flags.force,
10042
- commandLine: typedLine,
11069
+ secrets: command.secrets,
11070
+ rerun: rerunOf,
10043
11071
  notice: (text) => write.stderr(noticeLine(text, routed.json)),
10044
11072
  deliver: (data) => {
10045
- delivered = deliveredOutcome(command, data);
11073
+ delivered = () => dataOutcome(command, data, commandLine);
10046
11074
  }
10047
11075
  });
10048
11076
  const spill = spillTo({
10049
11077
  cliName: format.cliName,
10050
- commandName: command.name,
11078
+ label: commandLabel(routed.spec.path, format.cliName),
10051
11079
  env,
10052
11080
  clock,
10053
11081
  files: outputs.files,
@@ -10056,7 +11084,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10056
11084
  let served = false;
10057
11085
  let settled;
10058
11086
  try {
10059
- const outcome = await control.settle(skillOf(cli, installOf(cli)).notice({
11087
+ const outcome = await control.settle(skillOf$1(cli, installOf(cli)).notice({
10060
11088
  builtIn,
10061
11089
  bin: options.bin
10062
11090
  }, ctx).then(() => execute(command, input, ctx, {
@@ -10064,7 +11092,6 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10064
11092
  confirm: flags.confirm,
10065
11093
  yes: flags.yes,
10066
11094
  page: flags.page,
10067
- invocation,
10068
11095
  commandLine,
10069
11096
  started: async (outcome) => {
10070
11097
  write.stdout(await startedOutput(outcome, format, spill));
@@ -10088,7 +11115,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10088
11115
  };
10089
11116
  settled = {
10090
11117
  ok: false,
10091
- error: control.errorAfter(error),
11118
+ error: checkedCodes(control.errorAfter(error), routed.spec, cli),
10092
11119
  delivered
10093
11120
  };
10094
11121
  }
@@ -10172,6 +11199,10 @@ async function runWithProgress(cli, argv, options, write, stopNotice) {
10172
11199
  }
10173
11200
  async function routeAndRun(cli, argv, options, write, stopNotice) {
10174
11201
  const runAs = runAsOf(cli);
11202
+ const root = rootSpec(cli, {
11203
+ bin: options.bin,
11204
+ node: options.node
11205
+ });
10175
11206
  const format = {
10176
11207
  cliName: cli.name,
10177
11208
  env: options.env,
@@ -10182,16 +11213,10 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
10182
11213
  flags: []
10183
11214
  },
10184
11215
  json: false,
10185
- errors: cli.errors ?? {},
10186
- stop: options.signal
11216
+ errors: errorTableOf(cli),
11217
+ stop: options.signal,
11218
+ ...options.checkNextSteps === true ? { checkNextStep: (step) => nextStepProblem(root, runAs, step) } : {}
10187
11219
  };
10188
- const root = cli.commands === void 0 ? {
10189
- ...commandSpec(runAs, cli.command),
10190
- description: cli.description
10191
- } : groupSpec(runAs, cli.commands, builtInCommands(cli, {
10192
- bin: options.bin,
10193
- node: options.node
10194
- }));
10195
11220
  const routed = route(root, format.invocation);
10196
11221
  const misused = (node) => isAgent(options.env) ? {
10197
11222
  ...format,
@@ -10218,10 +11243,22 @@ async function ranAlone(result) {
10218
11243
  }
10219
11244
  //#endregion
10220
11245
  //#region ../agent-cli/dist/run-process.js
10221
- function written(stream, text) {
10222
- return new Promise((resolve) => stream.write(text, () => resolve()));
11246
+ const READER_GONE = /* @__PURE__ */ new Set(["EPIPE", "EOF"]);
11247
+ function writerTo(stream) {
11248
+ stream.on("error", (error) => {
11249
+ if (error.code === void 0 || !READER_GONE.has(error.code)) throw error;
11250
+ });
11251
+ return (text) => new Promise((resolve) => {
11252
+ if (stream.destroyed) {
11253
+ resolve();
11254
+ return;
11255
+ }
11256
+ stream.write(text, () => resolve());
11257
+ });
10223
11258
  }
10224
11259
  async function runProcess(cli) {
11260
+ const stdout = writerTo(process.stdout);
11261
+ const stderr = writerTo(process.stderr);
10225
11262
  const stop = new AbortController();
10226
11263
  const onSignal = (signal) => {
10227
11264
  if (stop.signal.aborted) process.exit(SIGNAL_EXIT_CODES[signal]);
@@ -10239,11 +11276,11 @@ async function runProcess(cli) {
10239
11276
  node: process.execPath,
10240
11277
  stdin: process.stdin
10241
11278
  }, {
10242
- stdout: (text) => process.stdout.write(text),
10243
- stderr: (text) => process.stderr.write(text),
10244
- result: async ({ stdout, stderr }) => {
10245
- await written(process.stdout, stdout);
10246
- await written(process.stderr, stderr);
11279
+ stdout: (text) => void stdout(text),
11280
+ stderr: (text) => void stderr(text),
11281
+ result: async (result) => {
11282
+ await stdout(result.stdout);
11283
+ await stderr(result.stderr);
10247
11284
  }
10248
11285
  }, "stopping… (Ctrl-C again to quit now)");
10249
11286
  process.exitCode = result.exitCode;
@@ -10255,16 +11292,44 @@ async function runProcess(cli) {
10255
11292
  }
10256
11293
  //#endregion
10257
11294
  //#region package.json
10258
- var version$1 = "0.17.0";
10259
- var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
10260
- //#endregion
10261
- //#region src/build.ts
10262
- const build = {
10263
- "sha": "8bb3938",
10264
- "dirty": false,
10265
- "builtAt": "2026-10-27T10:54:44.000Z"
11295
+ var package_default = {
11296
+ name: "@duyquangnvx/create-agent-cli",
11297
+ version: "0.18.0",
11298
+ description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
11299
+ type: "module",
11300
+ engines: { "node": ">=22.12.0" },
11301
+ bin: { "create-agent-cli": "./dist/bin.js" },
11302
+ files: [
11303
+ "dist",
11304
+ "partials",
11305
+ "template",
11306
+ "agents-section.md",
11307
+ "CHANGELOG.md"
11308
+ ],
11309
+ publishConfig: {
11310
+ "registry": "https://registry.npmjs.org/",
11311
+ "access": "public"
11312
+ },
11313
+ scripts: {
11314
+ "build": "node -e \"require('node:fs').copyFileSync('../agent-cli/agents-section.md', 'agents-section.md')\" && tsdown",
11315
+ "prepack": "node -e \"require('node:fs').copyFileSync('../../CHANGELOG.md', 'CHANGELOG.md')\""
11316
+ },
11317
+ devDependencies: {
11318
+ "@duyquangnvx/agent-cli": "workspace:*",
11319
+ "tsdown": "0.23.0",
11320
+ "zod": "4.6.7"
11321
+ }
10266
11322
  };
10267
11323
  //#endregion
11324
+ //#region src/agents-section.ts
11325
+ const AGENTS_SECTION_FILE = new URL("../agents-section.md", import.meta.url);
11326
+ function agentsSection() {
11327
+ return readFile(AGENTS_SECTION_FILE, "utf8");
11328
+ }
11329
+ //#endregion
11330
+ //#region src/define.ts
11331
+ const { defineCli, defineCommand, cliError } = agentCli({ errors: { STEP_FAILED: { class: "operator" } } });
11332
+ //#endregion
10268
11333
  //#region src/fill.ts
10269
11334
  /** `text` with each `{{key}}` replaced by its value; `quoted` escapes values that land inside a double-quoted string. */
10270
11335
  function fill(text, values, quoted) {
@@ -10279,29 +11344,55 @@ function fill(text, values, quoted) {
10279
11344
  const PARTIALS_DIR = fileURLToPath(new URL("../partials/", import.meta.url));
10280
11345
  const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
10281
11346
  const COMMANDS_ANCHOR = " commands: {";
10282
- const GLOBAL_INSTALL = ` install: {
10283
- mode: "global",
10284
- package: manifest.name,
10285
- registry: manifest.publishConfig.registry,
10286
- },
10287
- `;
10288
- const NO_SKILL_HUB = "<!-- Set `skill.source` in `defineCli` and `skillHub` in `scripts/release.ts` to the git URL of the Skill Hub each release commits the skill to, then put here the `! npx -y skills add` line that `node dist/bin.js doctor` gives as the `skill` row's fix. -->";
10289
- function installModeOf(skillHub, dependency, binName) {
11347
+ const GLOBAL_INSTALL = ` install: "global",\n`;
11348
+ const NO_SKILL_HUB = "<!-- Set `skill.source` in `defineCli` and `skillHub: { git }` in `scripts/release.ts` to the git URL of the Skill Hub each release commits the skill to, or set `skill.page` to the Skill Hub's page where each user copies their own install line and `skillHub: { api }` to the API Skill Hub each release publishes the skill to. Then put here what `node dist/bin.js doctor` gives in its `skill` row: the `! npx -y skills add` line of its fix, or the sentence naming the page. -->";
11349
+ function skillHubOf({ git, api, page }) {
11350
+ if (git !== void 0 && api !== void 0) throw new UsageError({
11351
+ code: "SKILL_HOMES_CONFLICT",
11352
+ message: "--skill-hub-git and --skill-hub-api each name the Skill Hub the skill goes to",
11353
+ hint: "pass one of them"
11354
+ });
11355
+ if (api !== void 0 && page === void 0) throw new UsageError({
11356
+ code: "SKILL_PAGE_MISSING",
11357
+ message: "--skill-hub-api needs --skill-page, the hub's page where each user copies their own install line",
11358
+ hint: "add --skill-page with the skill's page on the hub"
11359
+ });
11360
+ if (page !== void 0 && api === void 0) throw new UsageError({
11361
+ code: "SKILL_HUB_API_MISSING",
11362
+ message: "--skill-page names a page of an API Skill Hub, which --skill-hub-api names",
11363
+ hint: "add --skill-hub-api with the hub's URL"
11364
+ });
11365
+ if (git !== void 0) return {
11366
+ kind: "git",
11367
+ url: git
11368
+ };
11369
+ return api === void 0 || page === void 0 ? void 0 : {
11370
+ kind: "api",
11371
+ url: api,
11372
+ page
11373
+ };
11374
+ }
11375
+ function installModeOf(flags, dependency, binName) {
11376
+ const skillHub = skillHubOf(flags);
10290
11377
  if (skillHub !== void 0 && dependency) throw new UsageError({
10291
11378
  code: "SKILL_HOMES_CONFLICT",
10292
- message: "--skill-hub and --dependency each name where the skill comes from: a Dependency install ships it inside the package",
10293
- hint: "pass --dependency for a CLI each project pins, or --skill-hub for a global one whose skill each release commits to a Skill Hub"
11379
+ message: `--skill-hub-${skillHub.kind} and --dependency each name where the skill comes from: a Dependency install ships it inside the package`,
11380
+ hint: `pass --dependency for a CLI each project pins, or --skill-hub-${skillHub.kind} for a global one whose skill each release puts on a Skill Hub`
10294
11381
  });
10295
11382
  if ((skillHub !== void 0 || dependency) && !SKILL_NAME.test(binName)) throw new UsageError({
10296
11383
  code: "SKILL_NAME_INVALID",
10297
11384
  message: `${binName} cannot name a skill, whose names are lowercase letters, digits and single hyphens`,
10298
- hint: `choose a name such as my-cli, or leave out ${dependency ? "--dependency" : "--skill-hub"}`
11385
+ hint: `choose a name such as my-cli, or leave out ${dependency ? "--dependency" : `--skill-hub-${skillHub?.kind}`}`
10299
11386
  });
10300
11387
  return dependency ? { mode: "dependency" } : {
10301
11388
  mode: "global",
10302
11389
  skillHub
10303
11390
  };
10304
11391
  }
11392
+ function skillLineOf(skillHub, binName) {
11393
+ if (skillHub === void 0) return NO_SKILL_HUB;
11394
+ return skillHub.kind === "git" ? `\`! npx -y skills add ${skillHub.url} --skill ${binName} -g\`` : `The skill's line is your own: copy your install line from ${skillHub.page}, then type it.`;
11395
+ }
10305
11396
  async function partialsOf(mode, values) {
10306
11397
  const partial = async (name) => fill(await readFile(join(PARTIALS_DIR, mode, `${name}.md`), "utf8"), values, false).trimEnd();
10307
11398
  const [installLines, skillCheck, installSteps, updateSteps, sandbox] = await Promise.all([
@@ -10320,9 +11411,8 @@ async function partialsOf(mode, values) {
10320
11411
  };
10321
11412
  }
10322
11413
  async function globalSetup(skillHub, packageName, binName) {
10323
- const skillLine = skillHub === void 0 ? NO_SKILL_HUB : `\`! npx -y skills add ${skillHub} --skill ${binName} -g\``;
11414
+ const skillLine = skillLineOf(skillHub, binName);
10324
11415
  const runAs = runAsOf({ name: binName });
10325
- const url = JSON.stringify(skillHub);
10326
11416
  return {
10327
11417
  values: {
10328
11418
  runAs,
@@ -10337,15 +11427,18 @@ async function globalSetup(skillHub, packageName, binName) {
10337
11427
  edits: skillHub === void 0 ? [] : [{
10338
11428
  file: "src/cli.ts",
10339
11429
  find: COMMANDS_ANCHOR,
10340
- replace: ` skill: { source: ${url} },\n${COMMANDS_ANCHOR}`
11430
+ replace: ` skill: ${skillOf(skillHub)},\n${COMMANDS_ANCHOR}`
10341
11431
  }, {
10342
11432
  file: "scripts/release.ts",
10343
11433
  find: " smoke: {",
10344
- replace: ` skillHub: ${url},\n smoke: {`
11434
+ replace: ` skillHub: { ${skillHub.kind}: ${JSON.stringify(skillHub.url)} },\n smoke: {`
10345
11435
  }],
10346
11436
  files: []
10347
11437
  };
10348
11438
  }
11439
+ function skillOf(skillHub) {
11440
+ return skillHub.kind === "git" ? `{ source: ${JSON.stringify(skillHub.url)} }` : `{ page: ${JSON.stringify(skillHub.page)} }`;
11441
+ }
10349
11442
  async function dependencySetup(packageName, binName) {
10350
11443
  const runAs = runAsOf({
10351
11444
  name: binName,
@@ -10367,7 +11460,7 @@ async function dependencySetup(packageName, binName) {
10367
11460
  edits: [{
10368
11461
  file: "src/cli.ts",
10369
11462
  find: GLOBAL_INSTALL,
10370
- replace: ` install: { mode: "dependency", package: manifest.name },\n`
11463
+ replace: ` install: "dependency",\n`
10371
11464
  }, {
10372
11465
  file: "src/cli.ts",
10373
11466
  find: COMMANDS_ANCHOR,
@@ -10381,7 +11474,6 @@ async function installSetupOf(install, packageName, binName) {
10381
11474
  }
10382
11475
  //#endregion
10383
11476
  //#region src/steps.ts
10384
- const stepErrors = defineErrors({ STEP_FAILED: { class: "operator" } });
10385
11477
  const INSTALL = [{
10386
11478
  command: "pnpm",
10387
11479
  args: ["install"]
@@ -10450,7 +11542,7 @@ function tail(output) {
10450
11542
  return output.trimEnd().split(/\r?\n/).slice(-20).join("\n");
10451
11543
  }
10452
11544
  function stepFailed(problem, left, cwd, cause) {
10453
- return cliError(stepErrors, "STEP_FAILED", {
11545
+ return cliError("STEP_FAILED", {
10454
11546
  message: problem,
10455
11547
  hint: `fix the problem, then run in ${cwd}: ${left.map(stepLine).join(" && ")}`
10456
11548
  }, { cause });
@@ -10499,10 +11591,9 @@ async function writeTemplate(root, files, values) {
10499
11591
  }
10500
11592
  //#endregion
10501
11593
  //#region src/version.ts
10502
- const version = version$1;
11594
+ const version = package_default.version;
10503
11595
  //#endregion
10504
11596
  //#region src/create.ts
10505
- const { defineCommand } = agentCli();
10506
11597
  const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?([a-z0-9][a-z0-9._-]*)$/;
10507
11598
  function errorCode(error) {
10508
11599
  return error instanceof Error && "code" in error ? error.code : void 0;
@@ -10542,82 +11633,81 @@ async function ensureFree(path) {
10542
11633
  hint: "run `create-agent-cli <name>` with a name whose directory is free, or empty it first"
10543
11634
  });
10544
11635
  }
10545
- const create = defineCommand({
10546
- name: "create",
10547
- description: "Create a new Agent CLI project in ./<name>",
10548
- examples: [{
10549
- command: "create-agent-cli weather --description \"Weather forecasts\"",
10550
- note: "create ./weather, install dependencies and make the first commit"
10551
- }],
10552
- effect: "write",
10553
- 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") }),
10554
- options: object({
10555
- description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
10556
- install: boolean().default(true).describe("run pnpm install"),
10557
- git: boolean().default(true).describe("create a git repository with an initial commit"),
10558
- repository: url({ protocol: /^https$/ }).optional().describe("the https URL of the CLI's git repository"),
10559
- skillHub: url({ protocol: /^https$/ }).refine((url) => new URL(url).search === "" && new URL(url).hash === "", "must name the hub's repository with no query or fragment").refine((url) => new URL(url).pathname.endsWith(".git"), "must end in .git").optional().describe("the https git URL of the Skill Hub each release commits the CLI's skill to"),
10560
- dependency: boolean().default(false).describe("install the CLI per project, as a devDependency, with its skill shipped inside the package")
10561
- }),
10562
- plan: async ({ name, description, install, git, repository, skillHub, dependency }, ctx) => {
10563
- const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
10564
- const mode = installModeOf(skillHub, dependency, binName);
10565
- const path = resolve(ctx.cwd, binName);
10566
- await ensureFree(path);
10567
- const setup = await installSetupOf(mode, name, binName);
10568
- const values = {
10569
- packageName: name,
10570
- binName,
10571
- description,
10572
- debugVariable: debugVariable(binName),
10573
- runtimeVersion: version,
10574
- ...setup.values
10575
- };
10576
- return {
10577
- path,
10578
- values,
10579
- files: await templateFiles(values),
10580
- steps: stepsFor({
10581
- install,
10582
- git
10583
- }),
10584
- install,
10585
- repository,
10586
- edits: setup.edits,
10587
- manifestFiles: setup.files
10588
- };
10589
- },
10590
- apply: async (plan, ctx) => {
10591
- await mkdir(plan.path, { recursive: true });
10592
- await writeTemplate(plan.path, plan.files, plan.values);
10593
- if (plan.repository !== void 0) await setRepository(plan.path, plan.repository);
10594
- for (const edit of plan.edits) await applyEdit(plan.path, edit);
10595
- if (plan.manifestFiles.length > 0) await addFiles(plan.path, plan.manifestFiles);
10596
- await runSteps(plan.steps, plan.path, ctx);
10597
- return {
10598
- path: plan.path,
10599
- next: `cd ${plan.values.binName} && pnpm ${plan.install ? "test" : "install"}`
10600
- };
10601
- },
10602
- render: ({ path, next }) => `Created ${path}\nnext: ${next}`,
10603
- renderPlan: (plan) => [
10604
- `Would create ${plan.path}:`,
10605
- ...plan.files.map((file) => ` create ${file.target}`),
10606
- ...plan.steps.map((step) => ` run ${stepLine(step)}`)
10607
- ].join("\n")
10608
- });
10609
- //#endregion
10610
- //#region src/cli.ts
10611
- const { defineCli } = agentCli();
10612
11636
  //#endregion
10613
11637
  //#region src/bin.ts
10614
11638
  await runProcess(defineCli({
10615
- name: "create-agent-cli",
10616
- version,
10617
- description,
10618
- build,
10619
- errors: stepErrors,
10620
- command: create
11639
+ manifest: package_default,
11640
+ command: defineCommand({
11641
+ description: "Create a new Agent CLI project in ./<name>",
11642
+ examples: [{
11643
+ command: "create-agent-cli weather --description \"Weather forecasts\"",
11644
+ note: "create ./weather, install dependencies and make the first commit"
11645
+ }],
11646
+ effect: "write",
11647
+ errors: ["STEP_FAILED"],
11648
+ 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") }),
11649
+ options: object({
11650
+ description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
11651
+ install: boolean().default(true).describe("run pnpm install"),
11652
+ git: boolean().default(true).describe("create a git repository with an initial commit"),
11653
+ repository: url({ protocol: /^https$/ }).optional().describe("the https URL of the CLI's git repository"),
11654
+ skillHubGit: url({ protocol: /^https$/ }).refine((url) => new URL(url).search === "" && new URL(url).hash === "", "must name the hub's repository with no query or fragment").refine((url) => new URL(url).pathname.endsWith(".git"), "must end in .git").optional().describe("the https git URL of a git Skill Hub each release commits the CLI's skill to"),
11655
+ skillHubApi: url({ protocol: /^https$/ }).optional().describe("the https URL of an API Skill Hub each release publishes the CLI's skill to"),
11656
+ skillPage: url({ protocol: /^https$/ }).optional().describe("the https page of the skill on the API Skill Hub, where each user copies their own install line"),
11657
+ dependency: boolean().default(false).describe("install the CLI per project, as a devDependency, with its skill shipped inside the package")
11658
+ }),
11659
+ plan: async ({ name, description, install, git, repository, skillHubGit, skillHubApi, skillPage, dependency }, ctx) => {
11660
+ const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
11661
+ const mode = installModeOf({
11662
+ git: skillHubGit,
11663
+ api: skillHubApi,
11664
+ page: skillPage
11665
+ }, dependency, binName);
11666
+ const path = resolve(ctx.cwd, binName);
11667
+ await ensureFree(path);
11668
+ const setup = await installSetupOf(mode, name, binName);
11669
+ const values = {
11670
+ packageName: name,
11671
+ binName,
11672
+ description,
11673
+ debugVariable: debugVariable(binName),
11674
+ runtimeVersion: version,
11675
+ agentsSection: await agentsSection(),
11676
+ ...setup.values
11677
+ };
11678
+ return {
11679
+ path,
11680
+ values,
11681
+ files: await templateFiles(values),
11682
+ steps: stepsFor({
11683
+ install,
11684
+ git
11685
+ }),
11686
+ install,
11687
+ repository,
11688
+ edits: setup.edits,
11689
+ manifestFiles: setup.files
11690
+ };
11691
+ },
11692
+ apply: async (plan, ctx) => {
11693
+ await mkdir(plan.path, { recursive: true });
11694
+ await writeTemplate(plan.path, plan.files, plan.values);
11695
+ if (plan.repository !== void 0) await setRepository(plan.path, plan.repository);
11696
+ for (const edit of plan.edits) await applyEdit(plan.path, edit);
11697
+ if (plan.manifestFiles.length > 0) await addFiles(plan.path, plan.manifestFiles);
11698
+ await runSteps(plan.steps, plan.path, ctx);
11699
+ return {
11700
+ path: plan.path,
11701
+ next: `cd ${plan.values.binName} && pnpm ${plan.install ? "test" : "install"}`
11702
+ };
11703
+ },
11704
+ render: ({ path, next }) => `Created ${path}\nnext: ${next}`,
11705
+ renderPlan: (plan) => [
11706
+ `Would create ${plan.path}:`,
11707
+ ...plan.files.map((file) => ` create ${file.target}`),
11708
+ ...plan.steps.map((step) => ` run ${stepLine(step)}`)
11709
+ ].join("\n")
11710
+ })
10621
11711
  }));
10622
11712
  //#endregion
10623
11713
  export {};