@duyquangnvx/create-agent-cli 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { parseArgs, parseEnv } from "node:util";
3
3
  import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
- import { access, chmod, link, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
5
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
6
  import { constants as constants$1, homedir, tmpdir } from "node:os";
7
7
  import { spawn } from "node:child_process";
8
8
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -2425,6 +2425,138 @@ function handleIntersectionResults(result, left, right) {
2425
2425
  result.value = merged.data;
2426
2426
  return result;
2427
2427
  }
2428
+ const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
2429
+ $ZodType.init(inst, def);
2430
+ const memo = globalConfig.memoizer;
2431
+ memo?.attach(inst);
2432
+ inst._zod.parse = (payload, ctx) => {
2433
+ const input = payload.value;
2434
+ if (!isPlainObject(input)) {
2435
+ payload.issues.push({
2436
+ expected: "record",
2437
+ code: "invalid_type",
2438
+ input,
2439
+ inst
2440
+ });
2441
+ return payload;
2442
+ }
2443
+ const proms = [];
2444
+ const values = def.keyType._zod.values;
2445
+ if (values && !def.partial) {
2446
+ payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};
2447
+ const recordKeys = /* @__PURE__ */ new Set();
2448
+ for (const key of values) if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
2449
+ recordKeys.add(typeof key === "number" ? key.toString() : key);
2450
+ if (key === "__proto__") continue;
2451
+ const keyResult = def.keyType._zod.run({
2452
+ value: key,
2453
+ issues: []
2454
+ }, ctx);
2455
+ if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
2456
+ if (keyResult.issues.length) {
2457
+ payload.issues.push({
2458
+ code: "invalid_key",
2459
+ origin: "record",
2460
+ issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
2461
+ input: key,
2462
+ path: [key],
2463
+ inst
2464
+ });
2465
+ continue;
2466
+ }
2467
+ const outKey = keyResult.value;
2468
+ if (outKey === "__proto__") continue;
2469
+ const result = def.valueType._zod.run({
2470
+ value: input[key],
2471
+ issues: []
2472
+ }, ctx);
2473
+ if (result instanceof Promise) proms.push(result.then((result) => {
2474
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
2475
+ payload.value[outKey] = result.value;
2476
+ }));
2477
+ else {
2478
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
2479
+ payload.value[outKey] = result.value;
2480
+ }
2481
+ }
2482
+ let unrecognized;
2483
+ for (const key in input) if (!recordKeys.has(key)) {
2484
+ if (def.mode === "loose") {
2485
+ if (key === "__proto__") continue;
2486
+ payload.value[key] = input[key];
2487
+ } else {
2488
+ unrecognized = unrecognized ?? [];
2489
+ unrecognized.push(key);
2490
+ }
2491
+ }
2492
+ if (unrecognized && unrecognized.length > 0) payload.issues.push({
2493
+ code: "unrecognized_keys",
2494
+ input,
2495
+ inst,
2496
+ keys: unrecognized,
2497
+ continue: true
2498
+ });
2499
+ } else {
2500
+ payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};
2501
+ let unrecognized;
2502
+ for (const key of Reflect.ownKeys(input)) {
2503
+ if (key === "__proto__") continue;
2504
+ if (!Object.prototype.propertyIsEnumerable.call(input, key)) continue;
2505
+ let keyResult = def.keyType._zod.run({
2506
+ value: key,
2507
+ issues: []
2508
+ }, ctx);
2509
+ if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
2510
+ if (typeof key === "string" && number$1.test(key) && keyResult.issues.length) {
2511
+ const retryResult = def.keyType._zod.run({
2512
+ value: Number(key),
2513
+ issues: []
2514
+ }, ctx);
2515
+ if (retryResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
2516
+ if (retryResult.issues.length === 0) keyResult = retryResult;
2517
+ }
2518
+ if (keyResult.issues.length) {
2519
+ if (def.mode === "loose") payload.value[key] = input[key];
2520
+ else if (values) {
2521
+ unrecognized = unrecognized ?? [];
2522
+ unrecognized.push(key);
2523
+ } else payload.issues.push({
2524
+ code: "invalid_key",
2525
+ origin: "record",
2526
+ issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
2527
+ input: key,
2528
+ path: [key],
2529
+ inst
2530
+ });
2531
+ continue;
2532
+ }
2533
+ const outKey = keyResult.value;
2534
+ if (outKey === "__proto__") continue;
2535
+ const result = def.valueType._zod.run({
2536
+ value: input[key],
2537
+ issues: []
2538
+ }, ctx);
2539
+ if (result instanceof Promise) proms.push(result.then((result) => {
2540
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
2541
+ payload.value[outKey] = result.value;
2542
+ }));
2543
+ else {
2544
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
2545
+ payload.value[outKey] = result.value;
2546
+ }
2547
+ }
2548
+ if (unrecognized && unrecognized.length > 0) payload.issues.push({
2549
+ code: "unrecognized_keys",
2550
+ input,
2551
+ inst,
2552
+ keys: unrecognized,
2553
+ continue: true
2554
+ });
2555
+ }
2556
+ if (proms.length) return Promise.all(proms).then(() => payload);
2557
+ return payload;
2558
+ };
2559
+ });
2428
2560
  const $ZodEnum = /*@__PURE__*/ $constructor("$ZodEnum", (inst, def) => {
2429
2561
  $ZodType.init(inst, def);
2430
2562
  const values = getEnumValues(def.entries);
@@ -5093,6 +5225,9 @@ const ZodURL = /*@__PURE__*/ $constructor("ZodURL", (inst, def) => {
5093
5225
  $ZodURL.init(inst, def);
5094
5226
  ZodStringFormat.init(inst, def);
5095
5227
  });
5228
+ function url(params) {
5229
+ return /* @__PURE__ */ _url(ZodURL, params);
5230
+ }
5096
5231
  const ZodEmoji = /*@__PURE__*/ $constructor("ZodEmoji", (inst, def) => {
5097
5232
  $ZodEmoji.init(inst, def);
5098
5233
  ZodStringFormat.init(inst, def);
@@ -5367,6 +5502,28 @@ function intersection(left, right) {
5367
5502
  right
5368
5503
  });
5369
5504
  }
5505
+ const ZodRecord = /*@__PURE__*/ $constructor("ZodRecord", (inst, def) => {
5506
+ _ensureDefaultMemoizer();
5507
+ $ZodRecord.init(inst, def);
5508
+ ZodType.init(inst, def);
5509
+ inst._zod.processJSONSchema = (ctx, json, params) => recordProcessor(inst, ctx, json, params);
5510
+ inst.keyType = def.keyType;
5511
+ inst.valueType = def.valueType;
5512
+ });
5513
+ function record(keyType, valueType, params) {
5514
+ if (!valueType || !valueType._zod) return new ZodRecord({
5515
+ type: "record",
5516
+ keyType: string(),
5517
+ valueType: keyType,
5518
+ ...normalizeParams(valueType)
5519
+ });
5520
+ return new ZodRecord({
5521
+ type: "record",
5522
+ keyType,
5523
+ valueType,
5524
+ ...normalizeParams(params)
5525
+ });
5526
+ }
5370
5527
  const ZodEnum = /*@__PURE__*/ $constructor("ZodEnum", (inst, def) => {
5371
5528
  $ZodEnum.init(inst, def);
5372
5529
  ZodType.init(inst, def);
@@ -5582,6 +5739,146 @@ function untilAborted(signal) {
5582
5739
  });
5583
5740
  }
5584
5741
  //#endregion
5742
+ //#region ../agent-cli/dist/clock.js
5743
+ const realClock = {
5744
+ now: () => Date.now(),
5745
+ sleep: (ms, signal) => new Promise((resolve, reject) => {
5746
+ if (signal?.aborted === true) {
5747
+ reject(signal.reason);
5748
+ return;
5749
+ }
5750
+ const onAbort = () => {
5751
+ clearTimeout(timer);
5752
+ reject(signal?.reason);
5753
+ };
5754
+ const timer = setTimeout(() => {
5755
+ signal?.removeEventListener("abort", onAbort);
5756
+ resolve();
5757
+ }, ms);
5758
+ signal?.addEventListener("abort", onAbort, { once: true });
5759
+ })
5760
+ };
5761
+ /** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
5762
+ function after(clock, ms, cancel, then) {
5763
+ clock.sleep(ms, cancel).then(then, (error) => {
5764
+ if (!cancel.aborted) throw error;
5765
+ });
5766
+ }
5767
+ async function poll(clock, check, intervalMs, signal) {
5768
+ for (;;) {
5769
+ signal.throwIfAborted();
5770
+ const value = await check();
5771
+ if (value !== void 0) return value;
5772
+ await clock.sleep(intervalMs, signal);
5773
+ }
5774
+ }
5775
+ //#endregion
5776
+ //#region ../agent-cli/dist/errors.js
5777
+ var CliError = class extends Error {
5778
+ code;
5779
+ hint;
5780
+ details;
5781
+ retryAt;
5782
+ constructor({ code, message, hint, details, retryAt }, options) {
5783
+ super(message, options);
5784
+ this.name = "CliError";
5785
+ this.code = code;
5786
+ this.hint = hint;
5787
+ this.details = details;
5788
+ this.retryAt = retryAt;
5789
+ }
5790
+ };
5791
+ /** The caller called the Agent CLI wrong; the same call can't succeed. */
5792
+ var UsageError = class extends CliError {
5793
+ constructor(fields) {
5794
+ super(fields);
5795
+ this.name = "UsageError";
5796
+ }
5797
+ };
5798
+ const INTERNAL = "INTERNAL";
5799
+ function errnoOf(error) {
5800
+ return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
5801
+ }
5802
+ function isMissingFile(error) {
5803
+ return errnoOf(error) === "ENOENT";
5804
+ }
5805
+ function isNothingThere(error) {
5806
+ return isMissingFile(error) || errnoOf(error) === "ENOTDIR";
5807
+ }
5808
+ function debugVariable(cliName) {
5809
+ return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_DEBUG`;
5810
+ }
5811
+ function messageOf(error) {
5812
+ return error instanceof Error ? error.message : String(error);
5813
+ }
5814
+ function isDebug({ env, cliName }) {
5815
+ return Boolean(env[debugVariable(cliName)]);
5816
+ }
5817
+ function bugHint(format) {
5818
+ const bugs = format.bugs?.();
5819
+ if (isDebug(format)) {
5820
+ const where = "the stack trace shows where it failed";
5821
+ return bugs === void 0 ? where : `${where}; report it at ${bugs}`;
5822
+ }
5823
+ const rerun = `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
5824
+ return bugs === void 0 ? rerun : `${rerun}, then report it at ${bugs}`;
5825
+ }
5826
+ function fieldsOf$1(error, format) {
5827
+ if (error instanceof CliError) {
5828
+ const { code, message, hint, details, retryAt } = error;
5829
+ return {
5830
+ code,
5831
+ message,
5832
+ hint: format.isBug(error) ? bugHint(format) : hint,
5833
+ details,
5834
+ retryAt
5835
+ };
5836
+ }
5837
+ return {
5838
+ code: INTERNAL,
5839
+ message: messageOf(error),
5840
+ hint: bugHint(format)
5841
+ };
5842
+ }
5843
+ function causesOf(error) {
5844
+ return error instanceof Error && error.cause !== void 0 ? [error.cause, ...causesOf(error.cause)] : [];
5845
+ }
5846
+ function traceOf(error) {
5847
+ return error instanceof Error ? error.stack ?? error.message : String(error);
5848
+ }
5849
+ /** Under debug: a bug's stack trace, then every error's cause chain. */
5850
+ function debugTrace(error, format) {
5851
+ if (!isDebug(format)) return void 0;
5852
+ const lines = [...format.isBug(error) ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
5853
+ return lines.length === 0 ? void 0 : lines.join("\n");
5854
+ }
5855
+ /**
5856
+ * The errors a run failed with: the leaves of nested `AggregateError`s, or the one it threw. An
5857
+ * empty `AggregateError` is a leaf.
5858
+ */
5859
+ function errorsOf(error) {
5860
+ return error instanceof AggregateError && error.errors.length > 0 ? error.errors.flatMap(errorsOf) : [error];
5861
+ }
5862
+ function errorLines(error, format) {
5863
+ const fields = fieldsOf$1(error, format);
5864
+ const stack = debugTrace(error, format);
5865
+ const { style } = format;
5866
+ const label = style.red(format.isBug(error) ? "internal error:" : "error:");
5867
+ const trace = stack === void 0 ? "" : `${stack}\n`;
5868
+ return `${label} ${fields.message}\n${trace}${style.cyan("hint:")} ${fields.hint}\n`;
5869
+ }
5870
+ function formatRuntimeError(error, format) {
5871
+ const errors = errorsOf(error);
5872
+ if (format.json) {
5873
+ const printed = errors.map((each) => ({
5874
+ ...fieldsOf$1(each, format),
5875
+ stack: debugTrace(each, format)
5876
+ }));
5877
+ return `${JSON.stringify({ errors: printed })}\n`;
5878
+ }
5879
+ return errors.map((each) => errorLines(each, format)).join("");
5880
+ }
5881
+ //#endregion
5585
5882
  //#region ../agent-cli/dist/stdin.js
5586
5883
  const STDIN_META = "stdin";
5587
5884
  function isStdinSchema(schema) {
@@ -5597,13 +5894,37 @@ function readingLine(name, platform) {
5597
5894
  function isTerminal(source) {
5598
5895
  return typeof source !== "string" && "isTTY" in source && source.isTTY === true;
5599
5896
  }
5600
- /** The whole of `source` as UTF-8; rejects with the signal's reason once it aborts. */
5601
- function readStdin(source, signal) {
5897
+ const STDIN_STALLED = "STDIN_STALLED";
5898
+ /** How long a stdin that is not a terminal may send nothing, from the start of the read and from each chunk. */
5899
+ const STDIN_IDLE_MS = 3e4;
5900
+ function stdinStalled(callerLine) {
5901
+ return new CliError({
5902
+ code: STDIN_STALLED,
5903
+ message: `stdin sent nothing for ${STDIN_IDLE_MS} ms`,
5904
+ hint: `stdin never ended: close the pipe or heredoc that feeds \`${callerLine}\`, pass the text as an argument instead, or write a slow producer's output to a file and redirect it in`
5905
+ });
5906
+ }
5907
+ /**
5908
+ * The whole of `source` as UTF-8; rejects with the signal's reason once it aborts, and with
5909
+ * `idle.stalled()` once a stream that is not a terminal sends nothing for `STDIN_IDLE_MS`.
5910
+ */
5911
+ function readStdin(source, signal, idle) {
5602
5912
  if (typeof source === "string") return signal.aborted ? Promise.reject(signal.reason) : Promise.resolve(source);
5603
5913
  return new Promise((resolve, reject) => {
5604
5914
  const chunks = [];
5915
+ let idleWindow = new AbortController();
5916
+ const watchIdle = () => {
5917
+ idleWindow.abort();
5918
+ idleWindow = new AbortController();
5919
+ if (isTerminal(source)) return;
5920
+ after(idle.clock, STDIN_IDLE_MS, idleWindow.signal, () => {
5921
+ stop();
5922
+ reject(idle.stalled());
5923
+ });
5924
+ };
5605
5925
  const onData = (chunk) => {
5606
5926
  chunks.push(chunk instanceof Uint8Array ? chunk : Buffer.from(String(chunk)));
5927
+ watchIdle();
5607
5928
  };
5608
5929
  const onEnd = () => {
5609
5930
  stop();
@@ -5620,12 +5941,14 @@ function readStdin(source, signal) {
5620
5941
  const stop = () => {
5621
5942
  source.off("data", onData).off("end", onEnd).off("error", onError).pause();
5622
5943
  signal.removeEventListener("abort", onAbort);
5944
+ idleWindow.abort();
5623
5945
  };
5624
5946
  if (signal.aborted) {
5625
5947
  reject(signal.reason);
5626
5948
  return;
5627
5949
  }
5628
5950
  signal.addEventListener("abort", onAbort, { once: true });
5951
+ watchIdle();
5629
5952
  source.on("data", onData).once("end", onEnd).once("error", onError);
5630
5953
  });
5631
5954
  }
@@ -5699,11 +6022,11 @@ function optionField(key, schema) {
5699
6022
  usage
5700
6023
  };
5701
6024
  }
5702
- function fieldsOf$1(schema, toField) {
6025
+ function fieldsOf(schema, toField) {
5703
6026
  return schema === void 0 ? [] : Object.entries(schema.shape).map(([key, value]) => toField(key, value));
5704
6027
  }
5705
6028
  function argFields(schema) {
5706
- return fieldsOf$1(schema, argField);
6029
+ return fieldsOf(schema, argField);
5707
6030
  }
5708
6031
  function keysOf(fields) {
5709
6032
  return fields.map(({ key }) => `'${key}'`).join(", ");
@@ -5724,7 +6047,7 @@ function checkStdinFields(name, args, options) {
5724
6047
  }
5725
6048
  }
5726
6049
  function optionFields(schema) {
5727
- return fieldsOf$1(schema, optionField);
6050
+ return fieldsOf(schema, optionField);
5728
6051
  }
5729
6052
  /** Throws when an option is an array of booleans, which a switch cannot express. */
5730
6053
  function checkNoBooleanArrayOption(name, options) {
@@ -5732,142 +6055,45 @@ function checkNoBooleanArrayOption(name, options) {
5732
6055
  if (booleans.length > 0) throw new Error(`Command '${name}' declares boolean array option ${keysOf(booleans)}; a switch cannot repeat, use a boolean`);
5733
6056
  }
5734
6057
  //#endregion
5735
- //#region ../agent-cli/dist/errors.js
5736
- var CliError = class extends Error {
5737
- code;
5738
- hint;
5739
- details;
5740
- retryAt;
5741
- constructor({ code, message, hint, details, retryAt }, options) {
5742
- super(message, options);
5743
- this.name = "CliError";
5744
- this.code = code;
5745
- this.hint = hint;
5746
- this.details = details;
5747
- this.retryAt = retryAt;
5748
- }
5749
- };
5750
- /** The caller called the Agent CLI wrong; the same call can't succeed. */
5751
- var UsageError = class extends CliError {
5752
- constructor(fields) {
5753
- super(fields);
5754
- this.name = "UsageError";
5755
- }
5756
- };
5757
- const INTERNAL_CODE = "INTERNAL";
5758
- function errnoOf(error) {
5759
- return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
6058
+ //#region ../agent-cli/dist/tokenize.js
6059
+ const NEGATIVE_NUMBER = /^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/;
6060
+ function isOptionLike(word) {
6061
+ return word !== void 0 && word.length > 1 && word[0] === "-" && !NEGATIVE_NUMBER.test(word);
5760
6062
  }
5761
- function isMissingFile(error) {
5762
- return errnoOf(error) === "ENOENT";
6063
+ function parseArgsOptions(known) {
6064
+ return Object.fromEntries(known.map(({ flag, takes }) => [flag.slice(2), { type: takes === "value" ? "string" : "boolean" }]));
5763
6065
  }
5764
- function isNothingThere(error) {
5765
- return isMissingFile(error) || errnoOf(error) === "ENOTDIR";
6066
+ function flagItem(token, { flag, takes }, words) {
6067
+ if (takes !== "optional value" || token.inlineValue === true) return {
6068
+ kind: "flag",
6069
+ flag,
6070
+ value: token.value ?? true
6071
+ };
6072
+ const took = token.index + 1;
6073
+ const next = words[took];
6074
+ return next === void 0 || isOptionLike(next) ? {
6075
+ kind: "flag",
6076
+ flag,
6077
+ value: true
6078
+ } : {
6079
+ kind: "flag",
6080
+ flag,
6081
+ value: next,
6082
+ took
6083
+ };
5766
6084
  }
5767
- function debugVariable(cliName) {
5768
- return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_DEBUG`;
5769
- }
5770
- function messageOf(error) {
5771
- return error instanceof Error ? error.message : String(error);
5772
- }
5773
- function isDebug({ env, cliName }) {
5774
- return Boolean(env[debugVariable(cliName)]);
5775
- }
5776
- function bugHint(format) {
5777
- return isDebug(format) ? "the stack trace shows where it failed" : `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
5778
- }
5779
- function fieldsOf(error, format) {
5780
- if (error instanceof CliError) {
5781
- const { code, message, hint, details, retryAt } = error;
5782
- return {
5783
- code,
5784
- message,
5785
- hint: format.isBug(error) ? bugHint(format) : hint,
5786
- details,
5787
- retryAt
5788
- };
5789
- }
5790
- return {
5791
- code: INTERNAL_CODE,
5792
- message: messageOf(error),
5793
- hint: bugHint(format)
5794
- };
5795
- }
5796
- function causesOf(error) {
5797
- return error instanceof Error && error.cause !== void 0 ? [error.cause, ...causesOf(error.cause)] : [];
5798
- }
5799
- function traceOf(error) {
5800
- return error instanceof Error ? error.stack ?? error.message : String(error);
5801
- }
5802
- /** Under debug: a bug's stack trace, then every error's cause chain. */
5803
- function debugTrace(error, format) {
5804
- if (!isDebug(format)) return void 0;
5805
- const lines = [...format.isBug(error) ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
5806
- return lines.length === 0 ? void 0 : lines.join("\n");
5807
- }
5808
- /** The errors a run failed with: each one an `AggregateError` holds, or the one it threw. */
5809
- function errorsOf(error) {
5810
- return error instanceof AggregateError && error.errors.length > 0 ? error.errors : [error];
5811
- }
5812
- function errorLines(error, format) {
5813
- const fields = fieldsOf(error, format);
5814
- const stack = debugTrace(error, format);
5815
- const { style } = format;
5816
- const label = style.red(format.isBug(error) ? "internal error:" : "error:");
5817
- const trace = stack === void 0 ? "" : `${stack}\n`;
5818
- return `${label} ${fields.message}\n${trace}${style.cyan("hint:")} ${fields.hint}\n`;
5819
- }
5820
- function formatRuntimeError(error, format) {
5821
- const errors = errorsOf(error);
5822
- if (format.json) {
5823
- const printed = errors.map((each) => ({
5824
- ...fieldsOf(each, format),
5825
- stack: debugTrace(each, format)
5826
- }));
5827
- return `${JSON.stringify({ errors: printed })}\n`;
5828
- }
5829
- return errors.map((each) => errorLines(each, format)).join("");
5830
- }
5831
- //#endregion
5832
- //#region ../agent-cli/dist/tokenize.js
5833
- const NEGATIVE_NUMBER = /^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/;
5834
- function isOptionLike(word) {
5835
- return word !== void 0 && word.length > 1 && word[0] === "-" && !NEGATIVE_NUMBER.test(word);
5836
- }
5837
- function parseArgsOptions(known) {
5838
- return Object.fromEntries(known.map(({ flag, takes }) => [flag.slice(2), { type: takes === "value" ? "string" : "boolean" }]));
5839
- }
5840
- function flagItem(token, { flag, takes }, words) {
5841
- if (takes !== "optional value" || token.inlineValue === true) return {
5842
- kind: "flag",
5843
- flag,
5844
- value: token.value ?? true
5845
- };
5846
- const took = token.index + 1;
5847
- const next = words[took];
5848
- return next === void 0 || isOptionLike(next) ? {
5849
- kind: "flag",
5850
- flag,
5851
- value: true
5852
- } : {
5853
- kind: "flag",
5854
- flag,
5855
- value: next,
5856
- took
5857
- };
5858
- }
5859
- function itemOf(token, words, byFlag) {
5860
- const word = words[token.index] ?? "";
5861
- if (token.kind === "option-terminator") return { kind: "end" };
5862
- if (token.kind === "positional" || NEGATIVE_NUMBER.test(word)) return {
5863
- kind: "operand",
5864
- value: word
5865
- };
5866
- const flag = byFlag.get(token.rawName);
5867
- return flag === void 0 || flag.takes === "nothing" && token.inlineValue === true ? {
5868
- kind: "unknown option",
5869
- word
5870
- } : flagItem(token, flag, words);
6085
+ function itemOf(token, words, byFlag) {
6086
+ const word = words[token.index] ?? "";
6087
+ if (token.kind === "option-terminator") return { kind: "end" };
6088
+ if (token.kind === "positional" || NEGATIVE_NUMBER.test(word)) return {
6089
+ kind: "operand",
6090
+ value: word
6091
+ };
6092
+ const flag = byFlag.get(token.rawName);
6093
+ return flag === void 0 || flag.takes === "nothing" && token.inlineValue === true ? {
6094
+ kind: "unknown option",
6095
+ word
6096
+ } : flagItem(token, flag, words);
5871
6097
  }
5872
6098
  /** Splits a Command's words into operands, unknown options and the values of known flags. */
5873
6099
  function tokenize(words, known) {
@@ -5986,6 +6212,18 @@ function unknownOptionError(commandLine, invocation, flag, suggestions) {
5986
6212
  listed: "options"
5987
6213
  });
5988
6214
  }
6215
+ /** Text opening with `-` was read as an option, where the Command could have read it from stdin. */
6216
+ function stdinTextError(commandLine, invocation, word) {
6217
+ const withStdin = commandLineOf(invocation, {
6218
+ remove: [word],
6219
+ add: ["-"]
6220
+ });
6221
+ return new UsageError({
6222
+ code: UNKNOWN_OPTION,
6223
+ message: `unknown option '${word}' for \`${commandLine}\``,
6224
+ hint: `to pass text opening with \`-\`, pass \`-\` and send the text on stdin through a heredoc: \`${withStdin} <<'EOF'\`, then the text, then \`EOF\``
6225
+ });
6226
+ }
5989
6227
  function unknownCommandError(commandLine, name, suggestions) {
5990
6228
  return unknownError({
5991
6229
  code: UNKNOWN_COMMAND,
@@ -6012,7 +6250,7 @@ const COLUMN_SEPARATOR = " ";
6012
6250
  const UNSAFE_IN_COLUMN = /[\t\n\r]/;
6013
6251
  const PAGE_INPUT = object({
6014
6252
  limit: number().int().positive().default(DEFAULT_PAGE_LIMIT),
6015
- cursor: number().int().nonnegative().optional()
6253
+ cursor: string().min(1).optional()
6016
6254
  });
6017
6255
  var Page = class {
6018
6256
  items;
@@ -6100,12 +6338,16 @@ function cell(value) {
6100
6338
  function renderRows(rows, fields) {
6101
6339
  return `${[fields, ...rows.map((row) => fields.map((field) => cell(row[field])))].map((line) => line.join(COLUMN_SEPARATOR)).join("\n")}\n`;
6102
6340
  }
6341
+ function cursorWords(cursor) {
6342
+ const { flag } = PAGE_FLAGS.cursor;
6343
+ return cursor.startsWith("-") ? [`${flag}=${cursor}`] : [flag, cursor];
6344
+ }
6103
6345
  function printedPage(page, invocation) {
6104
6346
  return page.nextCursor === void 0 ? { items: page.items } : {
6105
6347
  items: page.items,
6106
6348
  next: commandLineOf(invocation, {
6107
6349
  remove: [PAGE_FLAGS.cursor.flag],
6108
- add: [PAGE_FLAGS.cursor.flag, String(page.nextCursor)]
6350
+ add: cursorWords(page.nextCursor)
6109
6351
  })
6110
6352
  };
6111
6353
  }
@@ -6234,71 +6476,50 @@ function isCommand(node) {
6234
6476
  return typeof node.effect === "string";
6235
6477
  }
6236
6478
  //#endregion
6237
- //#region ../agent-cli/dist/semver.js
6238
- const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
6239
- const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
6240
- function isVersion(text) {
6241
- return VERSION.test(text);
6242
- }
6243
- function compareIdentifiers(x, y) {
6244
- const xNumeric = /^\d+$/.test(x);
6245
- const yNumeric = /^\d+$/.test(y);
6246
- if (xNumeric && yNumeric) return Number(x) - Number(y);
6247
- if (xNumeric !== yNumeric) return xNumeric ? -1 : 1;
6248
- if (x === y) return 0;
6249
- return x < y ? -1 : 1;
6479
+ //#region ../agent-cli/dist/project.js
6480
+ const PROJECT_INVALID = "PROJECT_INVALID";
6481
+ function invalid(path, problem, options) {
6482
+ return new CliError({
6483
+ code: PROJECT_INVALID,
6484
+ message: `the Project file ${path} is invalid: ${problem}`,
6485
+ hint: `fix ${path}, then run the command again`
6486
+ }, options);
6250
6487
  }
6251
- function comparePrerelease(a, b) {
6252
- if (a === b) return 0;
6253
- if (a === void 0) return 1;
6254
- if (b === void 0) return -1;
6255
- const left = a.split(".");
6256
- const right = b.split(".");
6257
- for (let i = 0; i < Math.min(left.length, right.length); i += 1) {
6258
- const difference = compareIdentifiers(left[i] ?? "", right[i] ?? "");
6259
- if (difference !== 0) return difference;
6488
+ function readText(path, fileError) {
6489
+ try {
6490
+ return readFileSync(path, "utf8");
6491
+ } catch (error) {
6492
+ if (isMissingFile(error)) return void 0;
6493
+ throw fileError(error, {
6494
+ path,
6495
+ access: "read"
6496
+ });
6260
6497
  }
6261
- return left.length - right.length;
6262
6498
  }
6263
- /** Negative when `a` is older than `b`, positive when newer, 0 when the same release. */
6264
- function compareVersions(a, b) {
6265
- const left = VERSION.exec(a);
6266
- const right = VERSION.exec(b);
6267
- if (left === null || right === null) throw new Error(`cannot compare ${a} with ${b}: both must be X.Y.Z versions`);
6268
- for (const part of [
6269
- 1,
6270
- 2,
6271
- 3
6272
- ]) {
6273
- const difference = Number(left[part]) - Number(right[part]);
6274
- if (difference !== 0) return difference;
6499
+ function parsed(path, text, schema) {
6500
+ let json;
6501
+ try {
6502
+ json = JSON.parse(text);
6503
+ } catch (error) {
6504
+ throw invalid(path, messageOf(error), { cause: error });
6275
6505
  }
6276
- return comparePrerelease(left[4], right[4]);
6277
- }
6278
- /** Where `version` stands against `latest`. */
6279
- function standingOf(version, latest) {
6280
- const difference = compareVersions(version, latest);
6281
- if (difference < 0) return "behind";
6282
- return difference > 0 ? "ahead" : "current";
6283
- }
6284
- /** The minimum an `engines.node` range asks for, or undefined when it is not `>=X[.Y[.Z]]`. */
6285
- function engineMinimum(range) {
6286
- const match = ENGINE.exec(range.trim());
6287
- return match === null ? void 0 : match.slice(1).map((part) => Number(part ?? 0));
6506
+ const result = schema.safeParse(json);
6507
+ if (!result.success) throw invalid(path, prettifyError(result.error));
6508
+ return result.data;
6288
6509
  }
6289
- function meetsMinimum(version, minimum) {
6290
- const parts = version.split(".").map(Number);
6291
- for (const [i, wanted] of minimum.entries()) {
6292
- const found = parts[i] ?? 0;
6293
- if (found !== wanted) return found > wanted;
6294
- }
6295
- return true;
6510
+ function projectFrom(projectFile, folder, fileError) {
6511
+ const path = join(folder, projectFile.file);
6512
+ const text = readText(path, fileError);
6513
+ if (text !== void 0) return {
6514
+ root: folder,
6515
+ settings: parsed(path, text, projectFile.schema)
6516
+ };
6517
+ const parent = dirname(folder);
6518
+ return parent === folder ? void 0 : projectFrom(projectFile, parent, fileError);
6296
6519
  }
6297
- //#endregion
6298
- //#region ../agent-cli/dist/tail.js
6299
- /** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
6300
- function lastLines(output, count) {
6301
- return output.trim().split("\n").slice(-count).join("\n");
6520
+ /** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
6521
+ function projectOf(projectFile, cwd, fileError) {
6522
+ return projectFrom(projectFile, resolve(cwd), fileError);
6302
6523
  }
6303
6524
  //#endregion
6304
6525
  //#region ../agent-cli/dist/tools.js
@@ -6363,6 +6584,18 @@ function toolRows(tools, commands) {
6363
6584
  }));
6364
6585
  }
6365
6586
  //#endregion
6587
+ //#region ../agent-cli/dist/version.js
6588
+ /** The first line of `--version`: the version, and the commit and time of a stamped build. */
6589
+ function versionLine({ version, build }) {
6590
+ return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
6591
+ }
6592
+ //#endregion
6593
+ //#region ../agent-cli/dist/tail.js
6594
+ /** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
6595
+ function lastLines(output, count) {
6596
+ return output.trim().split("\n").slice(-count).join("\n");
6597
+ }
6598
+ //#endregion
6366
6599
  //#region ../agent-cli/dist/spawn.js
6367
6600
  const TOOL_FAILED = "TOOL_FAILED";
6368
6601
  const STDERR_TAIL_LINES$1 = 20;
@@ -6537,56 +6770,117 @@ function contextSpawn(run, defaults, fileError) {
6537
6770
  };
6538
6771
  }
6539
6772
  //#endregion
6540
- //#region ../agent-cli/dist/npm.js
6541
- const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
6542
- const REGISTRY_AUTH = "REGISTRY_AUTH";
6543
- const INSTALL_FAILED = "INSTALL_FAILED";
6544
- const NPM_FAILED = "NPM_FAILED";
6545
- const NO_LATEST = "NO_LATEST";
6546
- function npmContext(cli, install, ctx, node) {
6547
- return {
6548
- cliName: cli.name,
6549
- install,
6550
- env: ctx.env,
6551
- signal: ctx.signal,
6552
- node
6553
- };
6554
- }
6555
- const NPMJS = "https://registry.npmjs.org/";
6556
- const STDERR_TAIL_LINES = 10;
6557
- const RM_RETRIES = 3;
6558
- const NETWORK_CODES = /* @__PURE__ */ new Set([
6559
- "ENOTFOUND",
6560
- "EAI_AGAIN",
6561
- "ECONNREFUSED",
6562
- "ECONNRESET",
6563
- "ETIMEDOUT",
6564
- "ENETUNREACH",
6565
- "EHOSTUNREACH",
6566
- "ERR_SOCKET_TIMEOUT"
6567
- ]);
6568
- const AUTH_CODES = /* @__PURE__ */ new Set(["E401", "E403"]);
6569
- const NPM_CODE = /^npm (?:error|ERR!) code (\S+)$/m;
6570
- /** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
6571
- function registryFlags({ package: name, registry }) {
6572
- if (registry === NPMJS) return [];
6573
- const scope = /^(@[^/]+)\//.exec(name)?.[1];
6574
- return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
6773
+ //#region ../agent-cli/dist/built-ins/semver.js
6774
+ const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
6775
+ const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
6776
+ function isVersion(text) {
6777
+ return VERSION.test(text);
6575
6778
  }
6576
- /** The command a person runs to install the CLI globally from its registry. */
6577
- function installCommandLine(install, version = "latest") {
6578
- return [
6579
- "npm",
6580
- "install",
6581
- "--global",
6582
- `${install.package}@${version}`,
6583
- ...registryFlags(install)
6584
- ].join(" ");
6779
+ function compareIdentifiers(x, y) {
6780
+ const xNumeric = /^\d+$/.test(x);
6781
+ const yNumeric = /^\d+$/.test(y);
6782
+ if (xNumeric && yNumeric) return Number(x) - Number(y);
6783
+ if (xNumeric !== yNumeric) return xNumeric ? -1 : 1;
6784
+ if (x === y) return 0;
6785
+ return x < y ? -1 : 1;
6585
6786
  }
6586
- function npmOf(ctx, windows) {
6587
- const beside = ctx.node === void 0 ? void 0 : join(dirname(ctx.node), windows ? "npm.cmd" : "npm");
6588
- if (beside === void 0 || !existsSync(beside)) return {
6589
- path: "npm",
6787
+ function comparePrerelease(a, b) {
6788
+ if (a === b) return 0;
6789
+ if (a === void 0) return 1;
6790
+ if (b === void 0) return -1;
6791
+ const left = a.split(".");
6792
+ const right = b.split(".");
6793
+ for (let i = 0; i < Math.min(left.length, right.length); i += 1) {
6794
+ const difference = compareIdentifiers(left[i] ?? "", right[i] ?? "");
6795
+ if (difference !== 0) return difference;
6796
+ }
6797
+ return left.length - right.length;
6798
+ }
6799
+ /** Negative when `a` is older than `b`, positive when newer, 0 when the same release. */
6800
+ function compareVersions(a, b) {
6801
+ const left = VERSION.exec(a);
6802
+ const right = VERSION.exec(b);
6803
+ if (left === null || right === null) throw new Error(`cannot compare ${a} with ${b}: both must be X.Y.Z versions`);
6804
+ for (const part of [
6805
+ 1,
6806
+ 2,
6807
+ 3
6808
+ ]) {
6809
+ const difference = Number(left[part]) - Number(right[part]);
6810
+ if (difference !== 0) return difference;
6811
+ }
6812
+ return comparePrerelease(left[4], right[4]);
6813
+ }
6814
+ /** Where `version` stands against `latest`. */
6815
+ function standingOf(version, latest) {
6816
+ const difference = compareVersions(version, latest);
6817
+ if (difference < 0) return "behind";
6818
+ return difference > 0 ? "ahead" : "current";
6819
+ }
6820
+ /** The minimum an `engines.node` range asks for, or undefined when it is not `>=X[.Y[.Z]]`. */
6821
+ function engineMinimum(range) {
6822
+ const match = ENGINE.exec(range.trim());
6823
+ return match === null ? void 0 : match.slice(1).map((part) => Number(part ?? 0));
6824
+ }
6825
+ function meetsMinimum(version, minimum) {
6826
+ const parts = version.split(".").map(Number);
6827
+ for (const [i, wanted] of minimum.entries()) {
6828
+ const found = parts[i] ?? 0;
6829
+ if (found !== wanted) return found > wanted;
6830
+ }
6831
+ return true;
6832
+ }
6833
+ //#endregion
6834
+ //#region ../agent-cli/dist/built-ins/npm.js
6835
+ const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
6836
+ const REGISTRY_AUTH = "REGISTRY_AUTH";
6837
+ const INSTALL_FAILED = "INSTALL_FAILED";
6838
+ const NPM_FAILED = "NPM_FAILED";
6839
+ const NO_LATEST = "NO_LATEST";
6840
+ function npmContext(cli, install, ctx, node) {
6841
+ return {
6842
+ cliName: cli.name,
6843
+ install,
6844
+ env: ctx.env,
6845
+ signal: ctx.signal,
6846
+ node
6847
+ };
6848
+ }
6849
+ const NPMJS = "https://registry.npmjs.org/";
6850
+ const STDERR_TAIL_LINES = 10;
6851
+ const RM_RETRIES = 3;
6852
+ const NETWORK_CODES = /* @__PURE__ */ new Set([
6853
+ "ENOTFOUND",
6854
+ "EAI_AGAIN",
6855
+ "ECONNREFUSED",
6856
+ "ECONNRESET",
6857
+ "ETIMEDOUT",
6858
+ "ENETUNREACH",
6859
+ "EHOSTUNREACH",
6860
+ "ERR_SOCKET_TIMEOUT"
6861
+ ]);
6862
+ const AUTH_CODES = /* @__PURE__ */ new Set(["E401", "E403"]);
6863
+ const NPM_CODE = /^npm (?:error|ERR!) code (\S+)$/m;
6864
+ /** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
6865
+ function registryFlags({ package: name, registry }) {
6866
+ if (registry === NPMJS) return [];
6867
+ const scope = /^(@[^/]+)\//.exec(name)?.[1];
6868
+ return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
6869
+ }
6870
+ /** The command a person runs to install the CLI globally from its registry. */
6871
+ function installCommandLine(install, version = "latest") {
6872
+ return [
6873
+ "npm",
6874
+ "install",
6875
+ "--global",
6876
+ `${install.package}@${version}`,
6877
+ ...registryFlags(install)
6878
+ ].join(" ");
6879
+ }
6880
+ function npmOf(ctx, windows) {
6881
+ const beside = ctx.node === void 0 ? void 0 : join(dirname(ctx.node), windows ? "npm.cmd" : "npm");
6882
+ if (beside === void 0 || !existsSync(beside)) return {
6883
+ path: "npm",
6590
6884
  env: ctx.env
6591
6885
  };
6592
6886
  if (windows) return {
@@ -6714,7 +7008,7 @@ async function latestVersion(ctx) {
6714
7008
  return version;
6715
7009
  }
6716
7010
  //#endregion
6717
- //#region ../agent-cli/dist/global-install.js
7011
+ //#region ../agent-cli/dist/built-ins/global-install.js
6718
7012
  const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
6719
7013
  /** Where npm puts global packages under its global prefix. */
6720
7014
  function globalModulesDir(prefix) {
@@ -6755,101 +7049,747 @@ async function globalInstallOf(bin, ctx) {
6755
7049
  };
6756
7050
  }
6757
7051
  //#endregion
6758
- //#region ../agent-cli/dist/confirm.js
6759
- const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
6760
- const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
6761
- function checkConfirmation({ invocation, commandLine, given }, expected) {
6762
- if (given === expected) return;
6763
- const hint = `to confirm, run \`${commandLineOf(invocation, {
6764
- remove: [CONFIRM_FLAG.flag],
6765
- add: [CONFIRM_FLAG.flag, expected]
6766
- })}\``;
6767
- if (given === void 0) throw new UsageError({
6768
- code: CONFIRMATION_REQUIRED,
6769
- message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
6770
- hint
6771
- });
6772
- throw new UsageError({
6773
- code: CONFIRMATION_MISMATCH,
6774
- message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
6775
- hint
6776
- });
7052
+ //#region ../agent-cli/dist/manifest.js
7053
+ const link$1 = union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0);
7054
+ const manifestSchema = object({
7055
+ repository: link$1,
7056
+ bugs: link$1
7057
+ });
7058
+ const NO_MANIFEST = {};
7059
+ function manifestAt(packageRoot) {
7060
+ let manifest;
7061
+ try {
7062
+ manifest = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
7063
+ } catch {
7064
+ return NO_MANIFEST;
7065
+ }
7066
+ const parsed = manifestSchema.safeParse(manifest);
7067
+ return parsed.success ? parsed.data : NO_MANIFEST;
6777
7068
  }
6778
7069
  //#endregion
6779
- //#region ../agent-cli/dist/cost.js
6780
- const NOT_CONFIRMED = "NOT_CONFIRMED";
6781
- /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
6782
- function checkCost({ invocation, commandLine, yes }, cost) {
6783
- if (cost === void 0 || yes) return;
6784
- throw new CliError({
6785
- code: NOT_CONFIRMED,
6786
- message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
6787
- hint: `to approve the cost, run \`${commandLineOf(invocation, { add: [YES_FLAG.flag] })}\``
6788
- });
7070
+ //#region ../agent-cli/dist/built-ins/skills.js
7071
+ const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7072
+ /** The branch a release moves to the released commit, which holds the skill of that version. */
7073
+ const RELEASE_BRANCH$1 = "release";
7074
+ const SHORTHAND = /^(?:(?:github:)?[\w.-]+\/[\w.-]+|gitlab:[\w.-]+(?:\/[\w.-]+)+)$/;
7075
+ const GIT_URL_PROTOCOLS = /* @__PURE__ */ new Set([
7076
+ "https:",
7077
+ "http:",
7078
+ "ssh:",
7079
+ "git:"
7080
+ ]);
7081
+ const SCP_STYLE = /^[\w.-]+@[\w.-]+:[\w./-]+$/;
7082
+ const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
7083
+ const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
7084
+ /** The version a SKILL.md's frontmatter `metadata.version` states. */
7085
+ function skillVersionIn(skill) {
7086
+ const frontmatter = FRONTMATTER.exec(skill)?.[1];
7087
+ return frontmatter === void 0 ? void 0 : METADATA_VERSION.exec(`${frontmatter}\n`)?.[1];
6789
7088
  }
6790
- //#endregion
6791
- //#region ../agent-cli/dist/agent.js
6792
- const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
6793
- const isSet = (name) => (env) => (env[name] ?? "") !== "";
6794
- const equals = (name, value) => (env) => env[name] === value;
6795
- const MARKERS = [
6796
- (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
6797
- equals("AGENT", "amp"),
6798
- isSet("CODEX_SANDBOX"),
6799
- isSet("CODEX_CI"),
6800
- isSet("CODEX_THREAD_ID"),
6801
- isSet("GEMINI_CLI"),
6802
- isSet("COPILOT_CLI"),
6803
- isSet("OPENCODE"),
6804
- isSet("ANTIGRAVITY_AGENT"),
6805
- isSet("AUGMENT_AGENT"),
6806
- isSet("REPL_ID"),
6807
- isSet("CLAUDE_CODE_IS_COWORK"),
6808
- isSet("CLAUDECODE"),
6809
- isSet("CLAUDE_CODE"),
6810
- isSet("CURSOR_TRACE_ID"),
6811
- isSet("CURSOR_AGENT"),
6812
- equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
6813
- equals("TERM_PROGRAM", "kiro"),
6814
- (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
6815
- isSet("GOOSE_PROVIDER")
6816
- ];
6817
- /**
6818
- * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
6819
- * in a PTY, so a TTY alone doesn't mean a person is watching.
6820
- */
6821
- function isAgent(env) {
6822
- return MARKERS.some((marker) => marker(env));
7089
+ function homeOf(env) {
7090
+ return env.HOME ?? env.USERPROFILE ?? homedir();
7091
+ }
7092
+ async function copyAt(path, version) {
7093
+ let skill;
7094
+ try {
7095
+ skill = await readFile(join(path, "SKILL.md"), "utf8");
7096
+ } catch (error) {
7097
+ if (isMissingFile(error)) return;
7098
+ return {
7099
+ path,
7100
+ version: void 0,
7101
+ state: "unreadable"
7102
+ };
7103
+ }
7104
+ const stated = skillVersionIn(skill);
7105
+ if (stated === void 0 || !isVersion(stated) || !isVersion(version)) return {
7106
+ path,
7107
+ version: stated,
7108
+ state: "unversioned"
7109
+ };
7110
+ return {
7111
+ path,
7112
+ version: stated,
7113
+ state: standingOf(stated, version)
7114
+ };
7115
+ }
7116
+ async function skillCopies(cliName, version, { cwd, env }) {
7117
+ const paths = [cwd, homeOf(env)].flatMap((root) => SKILL_DIRS.map((dir) => join(root, ...dir, cliName)));
7118
+ const seen = /* @__PURE__ */ new Set();
7119
+ const copies = [];
7120
+ for (const path of paths) {
7121
+ const copy = await copyAt(path, version);
7122
+ const real = copy === void 0 ? void 0 : await realpath(path);
7123
+ if (copy !== void 0 && real !== void 0 && !seen.has(real)) {
7124
+ seen.add(real);
7125
+ copies.push(copy);
7126
+ }
7127
+ }
7128
+ return copies;
7129
+ }
7130
+ function sourceFromRepository(repository) {
7131
+ if (SHORTHAND.test(repository)) return `${repository}#${RELEASE_BRANCH$1}`;
7132
+ const url = repository.replace(/^git\+/, "");
7133
+ if (!(URL.canParse(url) && GIT_URL_PROTOCOLS.has(new URL(url).protocol)) && !SCP_STYLE.test(url)) return void 0;
7134
+ return `${url.endsWith(".git") ? url : `${url}.git`}#${RELEASE_BRANCH$1}`;
7135
+ }
7136
+ /** What the installer installs the CLI's skill from: the declared source, else the package's repository. */
7137
+ function skillSource({ skill }, packageRoot) {
7138
+ if (skill !== void 0) return skill.source;
7139
+ const { repository } = manifestAt(packageRoot);
7140
+ return repository === void 0 ? void 0 : sourceFromRepository(repository);
7141
+ }
7142
+ function fixFor(cliName, copies, source) {
7143
+ const has = (wanted) => copies.some(({ state }) => state === wanted);
7144
+ const update = `npx -y skills update ${cliName}`;
7145
+ const add = source === void 0 ? void 0 : `npx -y skills add ${source} --skill ${cliName} -g`;
7146
+ if (copies.length === 0) return add;
7147
+ if (has("unversioned") && add !== void 0) return has("behind") ? `${update} && ${add}` : add;
7148
+ return has("behind") || has("unversioned") ? update : void 0;
7149
+ }
7150
+ async function skillReport(cli, version, ctx) {
7151
+ const copies = await skillCopies(cli.name, version, ctx);
7152
+ return {
7153
+ copies,
7154
+ fix: fixFor(cli.name, copies, skillSource(cli, ctx.packageRoot))
7155
+ };
6823
7156
  }
6824
7157
  //#endregion
6825
- //#region ../agent-cli/dist/errno.js
6826
- const PERMISSION_DENIED = "PERMISSION_DENIED";
6827
- const DISK_FULL = "DISK_FULL";
6828
- const NOT_FOUND = "NOT_FOUND";
6829
- const ALREADY_EXISTS = "ALREADY_EXISTS";
6830
- const NOT_A_FILE = "NOT_A_FILE";
6831
- function permissionHint({ path, access, callerLine, env, platform }) {
6832
- if (platform === "win32") return `a program may hold ${path} open: close it, then run the same command again`;
6833
- if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
6834
- 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`}`;
7158
+ //#region ../agent-cli/dist/built-ins/doctor-rows.js
7159
+ function ok(message) {
7160
+ return {
7161
+ state: "ok",
7162
+ message
7163
+ };
6835
7164
  }
6836
- function fileErrorOf(scope) {
6837
- return (error, at) => classifyErrno(error, {
6838
- ...at,
6839
- ...scope
6840
- });
7165
+ function buildRow(cli) {
7166
+ return {
7167
+ name: "build",
7168
+ run: () => ok(versionLine(cli))
7169
+ };
6841
7170
  }
6842
- function classifyErrno(error, context) {
6843
- const { path } = context;
6844
- switch (errnoOf(error)) {
6845
- case "EACCES":
6846
- case "EPERM":
6847
- case "EROFS":
6848
- case "EBUSY": return new CliError({
6849
- code: PERMISSION_DENIED,
6850
- message: `permission denied: ${path}`,
6851
- hint: permissionHint(context)
6852
- }, { cause: error });
7171
+ function nodeRow(cli) {
7172
+ return {
7173
+ name: "node",
7174
+ run: () => {
7175
+ const found = process.versions.node;
7176
+ const range = cli.engines?.node;
7177
+ if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
7178
+ const minimum = engineMinimum(range);
7179
+ if (minimum === void 0) return {
7180
+ state: "problem",
7181
+ message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
7182
+ };
7183
+ if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
7184
+ const major = minimum[0];
7185
+ return {
7186
+ state: "problem",
7187
+ message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
7188
+ fix: `nvm install ${major} && nvm alias default ${major}`
7189
+ };
7190
+ }
7191
+ };
7192
+ }
7193
+ /** A row's thrown error as its problem: a `CliError` with its hint and code, anything else a bug. */
7194
+ function problemOfError(name, error) {
7195
+ if (error instanceof CliError) return {
7196
+ state: "problem",
7197
+ message: `${error.message}; ${error.hint}`,
7198
+ code: error.code
7199
+ };
7200
+ return {
7201
+ state: "problem",
7202
+ message: `the ${name} row hit a bug: ${messageOf(error)}`
7203
+ };
7204
+ }
7205
+ function readProject(projectFile, ctx) {
7206
+ if (projectFile === void 0) return {
7207
+ ok: true,
7208
+ project: void 0
7209
+ };
7210
+ try {
7211
+ return {
7212
+ ok: true,
7213
+ project: projectOf(projectFile, ctx.cwd, ctx.fileError)
7214
+ };
7215
+ } catch (error) {
7216
+ return {
7217
+ ok: false,
7218
+ error
7219
+ };
7220
+ }
7221
+ }
7222
+ function projectRow({ file }, read) {
7223
+ return {
7224
+ name: "project",
7225
+ run: (ctx) => {
7226
+ if (!read.ok) return problemOfError("project", read.error);
7227
+ return ok(read.project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(read.project.root, file));
7228
+ }
7229
+ };
7230
+ }
7231
+ function installRow(cli, { bin, node }) {
7232
+ return {
7233
+ name: "install",
7234
+ advisory: true,
7235
+ run: async (ctx) => {
7236
+ const { install } = cli;
7237
+ if (install === void 0) return ok(`${cli.name} declares no install, so it has no \`update\``);
7238
+ try {
7239
+ return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx, node))).prefix}`);
7240
+ } catch (error) {
7241
+ if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
7242
+ state: "problem",
7243
+ message: error.message,
7244
+ fix: installCommandLine(install)
7245
+ };
7246
+ throw error;
7247
+ }
7248
+ }
7249
+ };
7250
+ }
7251
+ function registryRow(cli, node) {
7252
+ return {
7253
+ name: "registry",
7254
+ advisory: true,
7255
+ run: async (ctx) => {
7256
+ const { install } = cli;
7257
+ if (install === void 0) return ok(`${cli.name} declares no registry to compare this build with`);
7258
+ const latest = await latestVersion(npmContext(cli, install, ctx, node));
7259
+ const standing = standingOf(cli.version, latest);
7260
+ const head = `${install.registry} has ${latest} as latest: this build`;
7261
+ if (standing === "behind") return {
7262
+ state: "problem",
7263
+ message: `${head} (${cli.version}) is older`,
7264
+ fix: `${cli.name} update`
7265
+ };
7266
+ return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
7267
+ }
7268
+ };
7269
+ }
7270
+ const SKILL_STATE_NOTES = {
7271
+ current: "the same as this build",
7272
+ behind: "older than this build",
7273
+ ahead: "newer than this build",
7274
+ unversioned: "states no metadata.version",
7275
+ unreadable: "could not be read"
7276
+ };
7277
+ function describeCopy({ path, version, state }) {
7278
+ return `${path}${version === void 0 ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
7279
+ }
7280
+ function skillRow(cli) {
7281
+ return {
7282
+ name: "skill",
7283
+ run: async (ctx) => {
7284
+ const { copies, fix } = await skillReport(cli, cli.version, ctx);
7285
+ const message = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
7286
+ return fix === void 0 ? ok(message) : {
7287
+ state: "ok",
7288
+ message,
7289
+ fix
7290
+ };
7291
+ }
7292
+ };
7293
+ }
7294
+ /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
7295
+ function doctorChecks(cli, launch, read) {
7296
+ return [
7297
+ buildRow(cli),
7298
+ nodeRow(cli),
7299
+ ...cli.project === void 0 ? [] : [projectRow(cli.project, read)],
7300
+ ...cli.doctor ?? [],
7301
+ ...toolRows(cli.tools ?? [], cli.commands),
7302
+ installRow(cli, launch),
7303
+ registryRow(cli, launch.node),
7304
+ skillRow(cli)
7305
+ ];
7306
+ }
7307
+ //#endregion
7308
+ //#region ../agent-cli/dist/built-ins/doctor.js
7309
+ const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
7310
+ const DEFAULT_DEADLINE_MS = 1e4;
7311
+ const MS_PER_SECOND = 1e3;
7312
+ const MIN_LABEL_WIDTH = 8;
7313
+ const reportSchema = object({
7314
+ rows: array(object({
7315
+ check: string(),
7316
+ state: _enum([
7317
+ "ok",
7318
+ "problem",
7319
+ "skipped"
7320
+ ]),
7321
+ message: string(),
7322
+ fix: string().optional(),
7323
+ code: string().optional()
7324
+ })),
7325
+ next: array(string())
7326
+ });
7327
+ function problemOf(check, error, { signal, deadline, ms }) {
7328
+ if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
7329
+ state: "problem",
7330
+ message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
7331
+ };
7332
+ return problemOfError(check.name, error);
7333
+ }
7334
+ function rowContext(ctx, signal, read) {
7335
+ return Object.create(ctx, {
7336
+ signal: { value: signal },
7337
+ project: { get: () => {
7338
+ if (!read.ok) throw read.error;
7339
+ return read.project;
7340
+ } }
7341
+ });
7342
+ }
7343
+ async function rowOf(check, ctx, read) {
7344
+ const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
7345
+ const signal = ctx.deadline(ms);
7346
+ let finding;
7347
+ try {
7348
+ finding = await Promise.race([Promise.resolve().then(() => check.run(rowContext(ctx, signal, read))), untilAborted(signal)]);
7349
+ } catch (error) {
7350
+ if (ctx.signal.aborted) return void 0;
7351
+ finding = !read.ok && error === read.error ? {
7352
+ state: "skipped",
7353
+ message: "the Project file is unusable; the project row says why"
7354
+ } : problemOf(check, error, {
7355
+ signal: ctx.signal,
7356
+ deadline: signal,
7357
+ ms
7358
+ });
7359
+ }
7360
+ return {
7361
+ row: {
7362
+ check: check.name,
7363
+ ...finding
7364
+ },
7365
+ advisory: check.advisory === true
7366
+ };
7367
+ }
7368
+ function nextOf(rows) {
7369
+ return [...new Set(rows.flatMap(({ fix }) => fix === void 0 ? [] : [fix]))];
7370
+ }
7371
+ function render({ rows, next }, style) {
7372
+ const width = Math.max(MIN_LABEL_WIDTH, ...rows.map(({ check }) => check.length)) + 2;
7373
+ const stateWidth = Math.max(...rows.map(({ state }) => state.length)) + 2;
7374
+ return [...rows.map(({ check, state, message }) => {
7375
+ const label = state === "problem" ? style.red(state) : state;
7376
+ return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
7377
+ }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
7378
+ }
7379
+ function failure(cliName, problems) {
7380
+ const errors = problems.map(({ check, message, fix, code }) => new CliError({
7381
+ code: code ?? "DOCTOR_PROBLEMS",
7382
+ message: `${cliName} doctor found a problem in ${check}: ${message}`,
7383
+ hint: fix === void 0 ? "the row says what is wrong" : `run ${fix}, then run doctor again`
7384
+ }));
7385
+ return new AggregateError(errors);
7386
+ }
7387
+ /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
7388
+ function doctorCommand(cli, launch) {
7389
+ return defineCommand$1({
7390
+ name: "doctor",
7391
+ description: "check the setup and say what fixes each problem",
7392
+ examples: [{
7393
+ command: `${cli.name} doctor`,
7394
+ note: "check the setup and list the commands that fix it"
7395
+ }, {
7396
+ command: `${cli.name} doctor --json`,
7397
+ note: "get the rows and fixes as data"
7398
+ }],
7399
+ effect: "read",
7400
+ output: reportSchema,
7401
+ run: async (_input, ctx) => {
7402
+ const read = readProject(cli.project, ctx);
7403
+ const checked = (await Promise.all(doctorChecks(cli, launch, read).map((check) => rowOf(check, ctx, read)))).filter((row) => row !== void 0);
7404
+ const rows = checked.map(({ row }) => row);
7405
+ const report = {
7406
+ rows,
7407
+ next: nextOf(rows)
7408
+ };
7409
+ if (ctx.signal.aborted) {
7410
+ ctx.deliver(report);
7411
+ ctx.signal.throwIfAborted();
7412
+ }
7413
+ const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
7414
+ if (problems.length > 0) {
7415
+ ctx.deliver(report);
7416
+ throw failure(cli.name, problems);
7417
+ }
7418
+ return report;
7419
+ },
7420
+ render
7421
+ });
7422
+ }
7423
+ //#endregion
7424
+ //#region ../agent-cli/dist/interrupts.js
7425
+ const INTERRUPTED = "INTERRUPTED";
7426
+ const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
7427
+ /** How long an interrupted Command may take to settle before the run ends without it. */
7428
+ const GRACE_MS = 3e3;
7429
+ const GRACE_OVER = Symbol("grace over");
7430
+ function interruptedError() {
7431
+ return new CliError({
7432
+ code: INTERRUPTED,
7433
+ message: "interrupted before it finished",
7434
+ hint: "run the command again"
7435
+ });
7436
+ }
7437
+ const WAIT_HINT = "run it again later, when the service it waits on may answer faster";
7438
+ function deadlineError(commandLine, ms, hint, bound = "deadline") {
7439
+ return new CliError({
7440
+ code: DEADLINE_EXCEEDED,
7441
+ message: `${commandLine} ran past its ${ms} ms ${bound}`,
7442
+ hint
7443
+ });
7444
+ }
7445
+ /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
7446
+ var RunControl = class {
7447
+ signal;
7448
+ #controller = new AbortController();
7449
+ #commandLine;
7450
+ #outer;
7451
+ #onOuterAbort = () => this.#controller.abort(interruptedError());
7452
+ /** Each running must-finish step, tracked until it settles, with its name. */
7453
+ #steps = /* @__PURE__ */ new Map();
7454
+ #progress;
7455
+ #clock;
7456
+ #disposed = new AbortController();
7457
+ /** The reasons this run's deadlines and timeouts aborted with, to tell them in a Command's error. */
7458
+ #abortReasons = /* @__PURE__ */ new WeakSet();
7459
+ #graceCancel = new AbortController();
7460
+ #endGrace = () => {};
7461
+ #graceOver = new Promise((resolve) => {
7462
+ this.#endGrace = resolve;
7463
+ });
7464
+ constructor({ commandLine, outer, clock, progress, runDeadline }) {
7465
+ this.#commandLine = commandLine;
7466
+ this.#outer = outer;
7467
+ this.#clock = clock;
7468
+ this.#progress = progress;
7469
+ this.signal = this.#controller.signal;
7470
+ this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
7471
+ if (outer?.aborted === true) this.#onOuterAbort();
7472
+ else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
7473
+ if (runDeadline !== void 0) {
7474
+ const { ms, hint } = runDeadline;
7475
+ after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, hint())));
7476
+ }
7477
+ }
7478
+ deadline(ms) {
7479
+ return AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
7480
+ }
7481
+ timeout(ms) {
7482
+ return this.#abortsAfter(ms, "timeout");
7483
+ }
7484
+ #abortsAfter(ms, bound) {
7485
+ const controller = new AbortController();
7486
+ after(this.#clock, ms, this.#disposed.signal, () => {
7487
+ const reason = deadlineError(this.#commandLine, ms, WAIT_HINT, bound);
7488
+ this.#abortReasons.add(reason);
7489
+ controller.abort(reason);
7490
+ });
7491
+ return controller.signal;
7492
+ }
7493
+ async runToEnd(name, work) {
7494
+ this.#graceCancel.abort();
7495
+ if (this.signal.aborted) this.#finishing(name);
7496
+ const step = work();
7497
+ const settled = step.then(() => {}, () => {});
7498
+ this.#steps.set(settled, name);
7499
+ try {
7500
+ return await step;
7501
+ } finally {
7502
+ this.#steps.delete(settled);
7503
+ this.#startGrace();
7504
+ }
7505
+ }
7506
+ /** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
7507
+ async settle(work) {
7508
+ try {
7509
+ const value = await Promise.race([work, this.#graceOver]);
7510
+ if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
7511
+ return value;
7512
+ } finally {
7513
+ await this.#stepsDone();
7514
+ this.close();
7515
+ }
7516
+ }
7517
+ /**
7518
+ * The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
7519
+ * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
7520
+ * Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's or timeout's
7521
+ * abort is reported as that abort, so a declared error keeps its own code.
7522
+ */
7523
+ errorAfter(error) {
7524
+ if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
7525
+ const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
7526
+ return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
7527
+ }
7528
+ #abortIn(error) {
7529
+ if (error instanceof CliError) return void 0;
7530
+ return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
7531
+ }
7532
+ #onStop() {
7533
+ for (const name of this.#steps.values()) this.#finishing(name);
7534
+ this.#startGrace();
7535
+ }
7536
+ #finishing(name) {
7537
+ this.#progress(`finishing ${name}`);
7538
+ }
7539
+ #startGrace() {
7540
+ if (this.signal.aborted && this.#steps.size === 0) {
7541
+ this.#graceCancel.abort();
7542
+ this.#graceCancel = new AbortController();
7543
+ after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
7544
+ }
7545
+ }
7546
+ async #stepsDone() {
7547
+ while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
7548
+ }
7549
+ /** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
7550
+ close() {
7551
+ this.#graceCancel.abort();
7552
+ this.#disposed.abort();
7553
+ this.#outer?.removeEventListener("abort", this.#onOuterAbort);
7554
+ }
7555
+ };
7556
+ //#endregion
7557
+ //#region ../agent-cli/dist/built-ins/upgrade-notes.js
7558
+ const RELEASE_HEADING = /^## v?(\S+)/;
7559
+ function releasesIn(changelog) {
7560
+ const releases = [];
7561
+ let current;
7562
+ for (const line of changelog.split(/\r?\n/)) {
7563
+ const heading = RELEASE_HEADING.exec(line);
7564
+ if (heading !== null) {
7565
+ const version = heading[1] ?? "";
7566
+ current = isVersion(version) ? {
7567
+ version,
7568
+ lines: []
7569
+ } : void 0;
7570
+ if (current !== void 0) releases.push(current);
7571
+ } else current?.lines.push(line);
7572
+ }
7573
+ return releases.map(({ version, lines }) => ({
7574
+ version,
7575
+ notes: lines.join("\n").trim()
7576
+ }));
7577
+ }
7578
+ /**
7579
+ * What changed in each release after `from` up to `to`, oldest first, from the CHANGELOG.md in
7580
+ * `packageDir`; none when the package ships no CHANGELOG.
7581
+ */
7582
+ async function upgradeNotes(packageDir, from, to) {
7583
+ let changelog;
7584
+ try {
7585
+ changelog = await readFile(join(packageDir, "CHANGELOG.md"), "utf8");
7586
+ } catch (error) {
7587
+ if (isMissingFile(error)) return [];
7588
+ throw error;
7589
+ }
7590
+ return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version));
7591
+ }
7592
+ //#endregion
7593
+ //#region ../agent-cli/dist/built-ins/update.js
7594
+ const updateSchema = object({
7595
+ from: string(),
7596
+ latest: string(),
7597
+ installed: object({
7598
+ version: string(),
7599
+ prefix: string()
7600
+ }).nullable(),
7601
+ upgrades: array(object({
7602
+ version: string(),
7603
+ notes: string()
7604
+ })),
7605
+ next: array(string())
7606
+ });
7607
+ function nothingToInstall({ from, latest }) {
7608
+ return standingOf(from, latest) === "current" ? `already on ${from}, the latest release: nothing to install` : `${from} is newer than the latest release ${latest}: nothing to install`;
7609
+ }
7610
+ function renderUpdate(cli, install, update) {
7611
+ const { installed } = update;
7612
+ const head = installed === null ? [nothingToInstall(update)] : [`installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`, ...update.upgrades.map(({ version, notes }) => `\nchanges in ${version}:\n${notes}`)];
7613
+ const next = update.next.map((command) => `next: ${command}, so the ${cli.name} skill describes ${update.latest}`);
7614
+ return (next.length === 0 ? head : [
7615
+ ...head,
7616
+ "",
7617
+ ...next
7618
+ ]).join("\n");
7619
+ }
7620
+ async function skillNext(cli, latest, ctx) {
7621
+ const { fix } = await skillReport(cli, latest, ctx);
7622
+ return fix === void 0 ? [] : [fix];
7623
+ }
7624
+ function interruptedAfterInstall(plan) {
7625
+ return new CliError({
7626
+ code: INTERRUPTED,
7627
+ message: `interrupted, but ${plan.package}@${plan.latest} had already been installed with the npm under ${plan.prefix}, replacing ${plan.from}`,
7628
+ hint: "nothing to run again: run the commands under next"
7629
+ });
7630
+ }
7631
+ /** The built-in `update`, for a CLI installed globally with npm. */
7632
+ function updateCommand(cli, install, { bin, node }) {
7633
+ return defineCommand$1({
7634
+ name: "update",
7635
+ description: "install the latest version of this CLI from its registry",
7636
+ examples: [{
7637
+ command: `${cli.name} update`,
7638
+ note: "install the latest version and show what changed"
7639
+ }, {
7640
+ command: `${cli.name} update --dry-run`,
7641
+ note: "show the version it would install"
7642
+ }],
7643
+ effect: "write",
7644
+ output: updateSchema,
7645
+ plan: async (_input, ctx) => {
7646
+ const npm = npmContext(cli, install, ctx, node);
7647
+ const copy = await globalInstallOf(bin, npm);
7648
+ return {
7649
+ package: install.package,
7650
+ from: cli.version,
7651
+ latest: await latestVersion(npm),
7652
+ prefix: copy.prefix,
7653
+ packageDir: copy.packageDir
7654
+ };
7655
+ },
7656
+ apply: async (plan, ctx) => {
7657
+ const { from, latest, prefix } = plan;
7658
+ if (standingOf(from, latest) !== "behind") return {
7659
+ from,
7660
+ latest,
7661
+ installed: null,
7662
+ upgrades: [],
7663
+ next: await skillNext(cli, latest, ctx)
7664
+ };
7665
+ ctx.signal.throwIfAborted();
7666
+ ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
7667
+ await ctx.runToEnd(`installing ${plan.package}@${latest}`, () => installGlobally(latest, npmContext(cli, install, ctx, node)));
7668
+ const update = {
7669
+ from,
7670
+ latest,
7671
+ installed: {
7672
+ version: latest,
7673
+ prefix
7674
+ },
7675
+ upgrades: await upgradeNotes(plan.packageDir, from, latest),
7676
+ next: await skillNext(cli, latest, ctx)
7677
+ };
7678
+ if (ctx.signal.aborted) {
7679
+ ctx.deliver(update);
7680
+ throw interruptedAfterInstall(plan);
7681
+ }
7682
+ return update;
7683
+ },
7684
+ render: (update) => renderUpdate(cli, install, update),
7685
+ renderPlan: (plan) => standingOf(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan)
7686
+ });
7687
+ }
7688
+ //#endregion
7689
+ //#region ../agent-cli/dist/built-ins/commands.js
7690
+ /** The Commands the Runtime adds to a multi-Command CLI: `update` only when it declares an install. */
7691
+ function builtInCommands(cli, launch) {
7692
+ return {
7693
+ doctor: doctorCommand(cli, launch),
7694
+ ...cli.install === void 0 ? {} : { update: updateCommand(cli, cli.install, launch) }
7695
+ };
7696
+ }
7697
+ //#endregion
7698
+ //#region ../agent-cli/dist/confirm.js
7699
+ const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
7700
+ const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
7701
+ function checkConfirmation({ invocation, commandLine, given }, expected) {
7702
+ if (given === expected) return;
7703
+ const hint = `to confirm, run \`${commandLineOf(invocation, {
7704
+ remove: [CONFIRM_FLAG.flag],
7705
+ add: [CONFIRM_FLAG.flag, expected]
7706
+ })}\``;
7707
+ if (given === void 0) throw new UsageError({
7708
+ code: CONFIRMATION_REQUIRED,
7709
+ message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
7710
+ hint
7711
+ });
7712
+ throw new UsageError({
7713
+ code: CONFIRMATION_MISMATCH,
7714
+ message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
7715
+ hint
7716
+ });
7717
+ }
7718
+ //#endregion
7719
+ //#region ../agent-cli/dist/cost.js
7720
+ const NOT_CONFIRMED = "NOT_CONFIRMED";
7721
+ /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
7722
+ function checkCost({ invocation, commandLine, yes }, cost) {
7723
+ if (cost === void 0 || yes) return;
7724
+ throw new CliError({
7725
+ code: NOT_CONFIRMED,
7726
+ message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
7727
+ hint: `to approve the cost, run \`${commandLineOf(invocation, { add: [YES_FLAG.flag] })}\``
7728
+ });
7729
+ }
7730
+ //#endregion
7731
+ //#region ../agent-cli/dist/agent.js
7732
+ const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
7733
+ const isSet = (name) => (env) => (env[name] ?? "") !== "";
7734
+ const equals = (name, value) => (env) => env[name] === value;
7735
+ const MARKERS = [
7736
+ (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
7737
+ equals("AGENT", "amp"),
7738
+ isSet("CODEX_SANDBOX"),
7739
+ isSet("CODEX_CI"),
7740
+ isSet("CODEX_THREAD_ID"),
7741
+ isSet("GEMINI_CLI"),
7742
+ isSet("COPILOT_CLI"),
7743
+ isSet("OPENCODE"),
7744
+ isSet("ANTIGRAVITY_AGENT"),
7745
+ isSet("AUGMENT_AGENT"),
7746
+ isSet("REPL_ID"),
7747
+ isSet("CLAUDE_CODE_IS_COWORK"),
7748
+ isSet("CLAUDECODE"),
7749
+ isSet("CLAUDE_CODE"),
7750
+ isSet("CURSOR_TRACE_ID"),
7751
+ isSet("CURSOR_AGENT"),
7752
+ equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
7753
+ equals("TERM_PROGRAM", "kiro"),
7754
+ (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
7755
+ isSet("GOOSE_PROVIDER")
7756
+ ];
7757
+ /**
7758
+ * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
7759
+ * in a PTY, so a TTY alone doesn't mean a person is watching.
7760
+ */
7761
+ function isAgent(env) {
7762
+ return MARKERS.some((marker) => marker(env));
7763
+ }
7764
+ //#endregion
7765
+ //#region ../agent-cli/dist/errno.js
7766
+ const PERMISSION_DENIED = "PERMISSION_DENIED";
7767
+ const DISK_FULL = "DISK_FULL";
7768
+ const NOT_FOUND = "NOT_FOUND";
7769
+ const ALREADY_EXISTS = "ALREADY_EXISTS";
7770
+ const NOT_A_FILE = "NOT_A_FILE";
7771
+ function permissionHint({ path, access, callerLine, env, platform }) {
7772
+ if (platform === "win32") return `a program may hold ${path} open: close it, then run the same command again`;
7773
+ if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
7774
+ 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`}`;
7775
+ }
7776
+ function fileErrorOf(scope) {
7777
+ return (error, at) => classifyErrno(error, {
7778
+ ...at,
7779
+ ...scope
7780
+ });
7781
+ }
7782
+ function classifyErrno(error, context) {
7783
+ const { path } = context;
7784
+ switch (errnoOf(error)) {
7785
+ case "EACCES":
7786
+ case "EPERM":
7787
+ case "EROFS":
7788
+ case "EBUSY": return new CliError({
7789
+ code: PERMISSION_DENIED,
7790
+ message: `permission denied: ${path}`,
7791
+ hint: permissionHint(context)
7792
+ }, { cause: error });
6853
7793
  case "ENOSPC":
6854
7794
  case "EDQUOT": return new CliError({
6855
7795
  code: DISK_FULL,
@@ -6877,159 +7817,6 @@ function classifyErrno(error, context) {
6877
7817
  }
6878
7818
  }
6879
7819
  //#endregion
6880
- //#region ../agent-cli/dist/clock.js
6881
- const realClock = {
6882
- now: () => Date.now(),
6883
- sleep: (ms, signal) => new Promise((resolve, reject) => {
6884
- if (signal?.aborted === true) {
6885
- reject(signal.reason);
6886
- return;
6887
- }
6888
- const onAbort = () => {
6889
- clearTimeout(timer);
6890
- reject(signal?.reason);
6891
- };
6892
- const timer = setTimeout(() => {
6893
- signal?.removeEventListener("abort", onAbort);
6894
- resolve();
6895
- }, ms);
6896
- signal?.addEventListener("abort", onAbort, { once: true });
6897
- })
6898
- };
6899
- /** Runs `then` once `ms` have passed on `clock`; aborting `cancel` drops it. */
6900
- function after(clock, ms, cancel, then) {
6901
- clock.sleep(ms, cancel).then(then, (error) => {
6902
- if (!cancel.aborted) throw error;
6903
- });
6904
- }
6905
- async function poll(clock, check, intervalMs, signal) {
6906
- for (;;) {
6907
- signal.throwIfAborted();
6908
- const value = await check();
6909
- if (value !== void 0) return value;
6910
- await clock.sleep(intervalMs, signal);
6911
- }
6912
- }
6913
- //#endregion
6914
- //#region ../agent-cli/dist/interrupts.js
6915
- const INTERRUPTED = "INTERRUPTED";
6916
- const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
6917
- /** How long an interrupted Command may take to settle before the run ends without it. */
6918
- const GRACE_MS = 3e3;
6919
- const GRACE_OVER = Symbol("grace over");
6920
- function interruptedError() {
6921
- return new CliError({
6922
- code: INTERRUPTED,
6923
- message: "interrupted before it finished",
6924
- hint: "run the command again"
6925
- });
6926
- }
6927
- const WAIT_HINT = "run it again later, when the service it waits on may answer faster";
6928
- function deadlineError(commandLine, ms, hint) {
6929
- return new CliError({
6930
- code: DEADLINE_EXCEEDED,
6931
- message: `${commandLine} ran past its ${ms} ms deadline`,
6932
- hint
6933
- });
6934
- }
6935
- /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
6936
- var RunControl = class {
6937
- signal;
6938
- #controller = new AbortController();
6939
- #commandLine;
6940
- #outer;
6941
- #onOuterAbort = () => this.#controller.abort(interruptedError());
6942
- /** Each running must-finish step, tracked until it settles, with its name. */
6943
- #steps = /* @__PURE__ */ new Map();
6944
- #progress;
6945
- #clock;
6946
- #disposed = new AbortController();
6947
- #deadlineHit;
6948
- #graceCancel = new AbortController();
6949
- #endGrace = () => {};
6950
- #graceOver = new Promise((resolve) => {
6951
- this.#endGrace = resolve;
6952
- });
6953
- constructor({ commandLine, outer, clock, progress, runDeadline }) {
6954
- this.#commandLine = commandLine;
6955
- this.#outer = outer;
6956
- this.#clock = clock;
6957
- this.#progress = progress;
6958
- this.signal = this.#controller.signal;
6959
- this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
6960
- if (outer?.aborted === true) this.#onOuterAbort();
6961
- else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
6962
- if (runDeadline !== void 0) {
6963
- const { ms, hint } = runDeadline;
6964
- after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, hint())));
6965
- }
6966
- }
6967
- deadline(ms) {
6968
- const controller = new AbortController();
6969
- after(this.#clock, ms, this.#disposed.signal, () => {
6970
- this.#deadlineHit ??= deadlineError(this.#commandLine, ms, WAIT_HINT);
6971
- controller.abort(this.#deadlineHit);
6972
- });
6973
- return AbortSignal.any([this.signal, controller.signal]);
6974
- }
6975
- async runToEnd(name, work) {
6976
- this.#graceCancel.abort();
6977
- if (this.signal.aborted) this.#finishing(name);
6978
- const step = work();
6979
- const settled = step.then(() => {}, () => {});
6980
- this.#steps.set(settled, name);
6981
- try {
6982
- return await step;
6983
- } finally {
6984
- this.#steps.delete(settled);
6985
- this.#startGrace();
6986
- }
6987
- }
6988
- /** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
6989
- async settle(work) {
6990
- try {
6991
- const value = await Promise.race([work, this.#graceOver]);
6992
- if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
6993
- return value;
6994
- } finally {
6995
- await this.#stepsDone();
6996
- this.close();
6997
- }
6998
- }
6999
- /**
7000
- * The error a run ends with: an interrupt or a missed deadline outranks what the Command threw,
7001
- * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
7002
- */
7003
- errorAfter(error) {
7004
- if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
7005
- if (this.#deadlineHit !== void 0 && !(error instanceof CliError)) return this.#deadlineHit;
7006
- return error;
7007
- }
7008
- #onStop() {
7009
- for (const name of this.#steps.values()) this.#finishing(name);
7010
- this.#startGrace();
7011
- }
7012
- #finishing(name) {
7013
- this.#progress(`finishing ${name}`);
7014
- }
7015
- #startGrace() {
7016
- if (this.signal.aborted && this.#steps.size === 0) {
7017
- this.#graceCancel.abort();
7018
- this.#graceCancel = new AbortController();
7019
- after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
7020
- }
7021
- }
7022
- async #stepsDone() {
7023
- while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
7024
- }
7025
- /** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
7026
- close() {
7027
- this.#graceCancel.abort();
7028
- this.#disposed.abort();
7029
- this.#outer?.removeEventListener("abort", this.#onOuterAbort);
7030
- }
7031
- };
7032
- //#endregion
7033
7820
  //#region ../agent-cli/dist/operator.js
7034
7821
  const NEEDS_OPERATOR = "NEEDS_OPERATOR";
7035
7822
  /** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
@@ -7160,12 +7947,19 @@ function realFiles() {
7160
7947
  force: true
7161
7948
  }),
7162
7949
  mkdir: async (path, options) => {
7950
+ const mode = options?.mode;
7163
7951
  if (options?.exclusive !== true) {
7164
- await mkdir(path, { recursive: true });
7952
+ await mkdir(path, {
7953
+ recursive: true,
7954
+ mode
7955
+ });
7165
7956
  return;
7166
7957
  }
7167
- await mkdir(dirname(path), { recursive: true });
7168
- await mkdir(path);
7958
+ await mkdir(dirname(path), {
7959
+ recursive: true,
7960
+ mode
7961
+ });
7962
+ await mkdir(path, { mode });
7169
7963
  },
7170
7964
  stat: async (path) => {
7171
7965
  try {
@@ -7265,62 +8059,16 @@ async function probeOutputs({ files, cwd, fileError }, outputs) {
7265
8059
  await Promise.all([...folders].map(async (folder) => {
7266
8060
  const probe = tempPathOf(join(folder, "outputs-probe"));
7267
8061
  try {
7268
- await files.mkdir(folder);
7269
- await files.write(probe, "");
7270
- await files.remove(probe);
7271
- } catch (error) {
7272
- throw fileError(error, {
7273
- path: folder,
7274
- access: "write"
7275
- });
7276
- }
7277
- }));
7278
- }
7279
- //#endregion
7280
- //#region ../agent-cli/dist/project.js
7281
- const PROJECT_INVALID = "PROJECT_INVALID";
7282
- function invalid(path, problem, options) {
7283
- return new CliError({
7284
- code: PROJECT_INVALID,
7285
- message: `the Project file ${path} is invalid: ${problem}`,
7286
- hint: `fix ${path}, then run the command again`
7287
- }, options);
7288
- }
7289
- function readText(path) {
7290
- try {
7291
- return readFileSync(path, "utf8");
7292
- } catch (error) {
7293
- if (isMissingFile(error)) return void 0;
7294
- throw error;
7295
- }
7296
- }
7297
- function parsed(path, text, schema) {
7298
- let json;
7299
- try {
7300
- json = JSON.parse(text);
7301
- } catch (error) {
7302
- throw invalid(path, messageOf(error), { cause: error });
7303
- }
7304
- const result = schema.safeParse(json);
7305
- if (!result.success) throw invalid(path, prettifyError(result.error));
7306
- return result.data;
7307
- }
7308
- function projectFrom({ file, schema }, folder) {
7309
- const path = join(folder, file);
7310
- const text = readText(path);
7311
- if (text !== void 0) return {
7312
- root: folder,
7313
- settings: parsed(path, text, schema)
7314
- };
7315
- const parent = dirname(folder);
7316
- return parent === folder ? void 0 : projectFrom({
7317
- file,
7318
- schema
7319
- }, parent);
7320
- }
7321
- /** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
7322
- function projectOf(projectFile, cwd) {
7323
- return projectFrom(projectFile, resolve(cwd));
8062
+ await files.mkdir(folder);
8063
+ await files.write(probe, "");
8064
+ await files.remove(probe);
8065
+ } catch (error) {
8066
+ throw fileError(error, {
8067
+ path: folder,
8068
+ access: "write"
8069
+ });
8070
+ }
8071
+ }));
7324
8072
  }
7325
8073
  //#endregion
7326
8074
  //#region ../agent-cli/dist/exit-classes.js
@@ -7335,46 +8083,44 @@ const EXIT_CODES = {
7335
8083
  wait: 75,
7336
8084
  bug: 70
7337
8085
  };
7338
- /** The codes the Runtime and its built-in Commands raise, each with the class that must act on it. */
7339
- function runtimeCodes() {
7340
- return {
7341
- [INVALID_INPUT]: "caller",
7342
- [UNKNOWN_OPTION]: "caller",
7343
- [UNKNOWN_COMMAND]: "caller",
7344
- [UNKNOWN_FIELD]: "caller",
7345
- [CONFIRMATION_REQUIRED]: "caller",
7346
- [CONFIRMATION_MISMATCH]: "caller",
7347
- [NOT_CONFIRMED]: "operator",
7348
- [NEEDS_OPERATOR]: "operator",
7349
- [OUTPUT_EXISTS]: "caller",
7350
- [INTERRUPTED]: "interrupted",
7351
- [DEADLINE_EXCEEDED]: "wait",
7352
- [INTERNAL_CODE]: "bug",
7353
- [PERMISSION_DENIED]: "operator",
7354
- [DISK_FULL]: "operator",
7355
- [NOT_FOUND]: "caller",
7356
- [ALREADY_EXISTS]: "caller",
7357
- [NOT_A_FILE]: "caller",
7358
- [TOOL_FAILED]: "operator",
7359
- [TOOL_MISSING]: "operator",
7360
- [PROJECT_INVALID]: "operator",
7361
- [DOCTOR_PROBLEMS]: "operator",
7362
- [NOT_GLOBAL_INSTALL]: "operator",
7363
- [REGISTRY_UNREACHABLE]: "operator",
7364
- [REGISTRY_AUTH]: "operator",
7365
- [INSTALL_FAILED]: "operator",
7366
- [NPM_FAILED]: "operator",
7367
- [NO_LATEST]: "source"
7368
- };
7369
- }
8086
+ const RUNTIME_EXIT_CLASSES = {
8087
+ [INVALID_INPUT]: "caller",
8088
+ [UNKNOWN_OPTION]: "caller",
8089
+ [UNKNOWN_COMMAND]: "caller",
8090
+ [UNKNOWN_FIELD]: "caller",
8091
+ [CONFIRMATION_REQUIRED]: "caller",
8092
+ [CONFIRMATION_MISMATCH]: "caller",
8093
+ [NOT_CONFIRMED]: "operator",
8094
+ [NEEDS_OPERATOR]: "operator",
8095
+ [OUTPUT_EXISTS]: "caller",
8096
+ [STDIN_STALLED]: "caller",
8097
+ [INTERRUPTED]: "interrupted",
8098
+ [DEADLINE_EXCEEDED]: "wait",
8099
+ [INTERNAL]: "bug",
8100
+ [PERMISSION_DENIED]: "operator",
8101
+ [DISK_FULL]: "operator",
8102
+ [NOT_FOUND]: "caller",
8103
+ [ALREADY_EXISTS]: "caller",
8104
+ [NOT_A_FILE]: "caller",
8105
+ [TOOL_FAILED]: "operator",
8106
+ [TOOL_MISSING]: "operator",
8107
+ [PROJECT_INVALID]: "operator",
8108
+ [DOCTOR_PROBLEMS]: "operator",
8109
+ [NOT_GLOBAL_INSTALL]: "operator",
8110
+ [REGISTRY_UNREACHABLE]: "operator",
8111
+ [REGISTRY_AUTH]: "operator",
8112
+ [INSTALL_FAILED]: "operator",
8113
+ [NPM_FAILED]: "operator",
8114
+ [NO_LATEST]: "source"
8115
+ };
7370
8116
  /** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
7371
8117
  function runtimeCodesIn(errors) {
7372
- return Object.keys(errors).filter((code) => Object.hasOwn(runtimeCodes(), code));
8118
+ return Object.keys(errors).filter((code) => Object.hasOwn(RUNTIME_EXIT_CLASSES, code));
7373
8119
  }
7374
8120
  function exitClassOfOne(error, errors) {
7375
8121
  if (!(error instanceof CliError)) return "bug";
7376
8122
  if (error instanceof UsageError) return "caller";
7377
- return runtimeCodes()[error.code] ?? errors[error.code]?.class ?? "bug";
8123
+ return RUNTIME_EXIT_CLASSES[error.code] ?? errors[error.code]?.class ?? "bug";
7378
8124
  }
7379
8125
  function isBug(error, errors) {
7380
8126
  return exitClassOfOne(error, errors) === "bug";
@@ -7418,6 +8164,50 @@ function exitCodeOf(error, errors, stop) {
7418
8164
  return exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass];
7419
8165
  }
7420
8166
  //#endregion
8167
+ //#region ../agent-cli/dist/agent-cli.js
8168
+ function checkOwnCodes(cli) {
8169
+ const redeclared = runtimeCodesIn(cli.errors ?? {});
8170
+ if (redeclared.length > 0) throw new Error(`${cli.name} declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
8171
+ }
8172
+ function defineCli$1(cli) {
8173
+ checkOwnCodes(cli);
8174
+ if (cli.commands === void 0) return cli;
8175
+ const { commands } = cli;
8176
+ const taken = Object.keys(builtInCommands(cli, {
8177
+ bin: void 0,
8178
+ node: void 0
8179
+ })).filter((name) => commands[name] !== void 0);
8180
+ if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
8181
+ return cli;
8182
+ }
8183
+ /**
8184
+ * Declares the CLI's ports and its Project file's settings once; the `defineCommand` and
8185
+ * `defineCli` it returns type `ctx.ports` and `ctx.project`.
8186
+ */
8187
+ function agentCli() {
8188
+ return {
8189
+ defineCommand: defineCommandFor(),
8190
+ defineCli: defineCli$1
8191
+ };
8192
+ }
8193
+ //#endregion
8194
+ //#region ../agent-cli/dist/error-table.js
8195
+ /** Keeps each code's literal class and `details` schema, so `cliError` can type its throw sites. */
8196
+ function defineErrors(table) {
8197
+ return table;
8198
+ }
8199
+ /**
8200
+ * A `CliError` for a code of `table`, whose `details` must match the code's schema. The `hint`
8201
+ * names the next command to run and says whether running it again can help; `retryAt` (ISO 8601)
8202
+ * is allowed only on a wait-class code.
8203
+ */
8204
+ function cliError(_table, code, init, options) {
8205
+ return new CliError({
8206
+ ...init,
8207
+ code
8208
+ }, options);
8209
+ }
8210
+ //#endregion
7421
8211
  //#region ../agent-cli/dist/command-spec.js
7422
8212
  /** The group's Commands and groups that help and suggestions may name: hidden Commands left out. */
7423
8213
  function visibleCommands({ commands }) {
@@ -7435,12 +8225,18 @@ function declaredOf(command) {
7435
8225
  cost: command.cost !== void 0
7436
8226
  };
7437
8227
  }
7438
- function commandSpec(commandLine, command) {
8228
+ const SINGLE_COMMAND = {
8229
+ path: [],
8230
+ builtIn: false
8231
+ };
8232
+ function commandSpec(commandLine, command, { path, builtIn } = SINGLE_COMMAND) {
7439
8233
  const list = command.rows === void 0 ? void 0 : listSpec(command.rows);
7440
8234
  return {
7441
8235
  kind: "command",
7442
8236
  commandLine,
8237
+ path,
7443
8238
  command,
8239
+ builtIn,
7444
8240
  description: command.description,
7445
8241
  help: command.help,
7446
8242
  hidden: command.hidden === true,
@@ -7454,15 +8250,34 @@ function commandSpec(commandLine, command) {
7454
8250
  output: list?.output ?? command.output
7455
8251
  };
7456
8252
  }
7457
- function groupSpec(commandLine, commands) {
8253
+ function group(commandLine, commands) {
7458
8254
  return {
7459
8255
  kind: "group",
7460
8256
  commandLine,
7461
- commands: Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node)]))
8257
+ commands
7462
8258
  };
7463
8259
  }
7464
- function specOf(commandLine, node) {
7465
- return isCommand(node) ? commandSpec(commandLine, node) : groupSpec(commandLine, node);
8260
+ /** The root group of a multi-Command CLI: the author's Commands, then the Built-in Commands. */
8261
+ function groupSpec(commandLine, commands, builtIns) {
8262
+ return group(commandLine, {
8263
+ ...specsOf(commandLine, commands, {
8264
+ path: [],
8265
+ builtIn: false
8266
+ }),
8267
+ ...specsOf(commandLine, builtIns, {
8268
+ path: [],
8269
+ builtIn: true
8270
+ })
8271
+ });
8272
+ }
8273
+ function specsOf(commandLine, commands, { path, builtIn }) {
8274
+ return Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node, {
8275
+ path: [...path, key],
8276
+ builtIn
8277
+ })]));
8278
+ }
8279
+ function specOf(commandLine, node, place) {
8280
+ return isCommand(node) ? commandSpec(commandLine, node, place) : group(commandLine, specsOf(commandLine, node, place));
7466
8281
  }
7467
8282
  //#endregion
7468
8283
  //#region ../agent-cli/dist/help.js
@@ -7487,9 +8302,10 @@ function commandRows(group) {
7487
8302
  function lines(...parts) {
7488
8303
  return `${parts.join("\n")}\n`;
7489
8304
  }
7490
- function listingLines(heading, group, style) {
8305
+ function listingLines(heading, group, style, description) {
7491
8306
  return [
7492
8307
  style.bold(heading),
8308
+ ...description === void 0 ? [] : [description],
7493
8309
  ...section("Commands", commandRows(group), style),
7494
8310
  "",
7495
8311
  `Run \`${group.commandLine} <command> --help\` for its examples and input.`
@@ -7522,753 +8338,350 @@ function examplesSection(examples, style) {
7522
8338
  }
7523
8339
  /** The CLI's own help: a listing or its single Command's help, ending with exit codes and examples. */
7524
8340
  function cliHelp(cli, root, style) {
7525
- return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${root.commandLine} ${cli.version}`, root, style), ...section("Exit codes", exitCodeRows(cli), style), ...examplesSection(rootExamples(root), style));
8341
+ return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${root.commandLine} ${cli.version}`, root, style, cli.description), ...section("Exit codes", exitCodeRows(cli), style), ...examplesSection(rootExamples(root), style));
7526
8342
  }
7527
8343
  function groupHelp(group, style) {
7528
8344
  return lines(...listingLines(group.commandLine, group, style));
7529
8345
  }
7530
- const EFFECT_NOTES = {
7531
- read: "changes nothing",
7532
- write: "changes something; show the Plan with --dry-run",
7533
- destructive: "changes something hard to undo; show the Plan with --dry-run, run with --confirm",
7534
- serve: "changes nothing; prints its data at once, then runs until Ctrl-C, which exits 0"
7535
- };
7536
- const OPERATOR_NOTE = "only a person at a terminal runs it; a Coding agent asks the operator to type it after `!`";
7537
- function choicesOf(field) {
7538
- return field.choices === void 0 ? [] : [`one of: ${field.choices.join(", ")}`];
7539
- }
7540
- function defaultsOf(field) {
7541
- if (field.defaultValue === void 0) return [];
7542
- if (field.type !== "boolean") return [`default: ${JSON.stringify(field.defaultValue)}`];
7543
- return field.defaultValue === true ? ["on by default; this flag turns it off"] : [];
7544
- }
7545
- function fieldRow(field, notes) {
7546
- const all = [...field.required ? ["required"] : [], ...notes];
7547
- const suffix = all.length === 0 ? "" : `(${all.join("; ")})`;
7548
- return [field.usage, [field.description ?? "", suffix].filter((part) => part !== "").join(" ")];
7549
- }
7550
- function argRow(field) {
7551
- return fieldRow(field, [...choicesOf(field), ...defaultsOf(field)]);
7552
- }
7553
- function optionRow(field) {
7554
- return fieldRow(field, defaultsOf(field));
7555
- }
7556
- function flagRow({ usage, description }) {
7557
- return [usage, description];
7558
- }
7559
- function commandLines(spec, style, { examples }) {
7560
- return [
7561
- style.bold(spec.commandLine),
7562
- spec.description,
7563
- ...examples ? examplesSection(spec.examples, style) : [],
7564
- ...section("Arguments", spec.args.map(argRow), style),
7565
- ...section("Options", spec.options.map(optionRow), style),
7566
- ...spec.help === void 0 ? [] : ["", spec.help],
7567
- "",
7568
- `${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`,
7569
- ...spec.operator ? [`${style.bold("Operator Command:")} ${OPERATOR_NOTE}`] : [],
7570
- ...section("Standard flags", spec.standardFlags.map(flagRow), style)
7571
- ];
7572
- }
7573
- function commandHelp(spec, style) {
7574
- return lines(...commandLines(spec, style, { examples: true }));
7575
- }
7576
- //#endregion
7577
- //#region ../agent-cli/dist/help-json.js
7578
- function inputSchema(schema) {
7579
- return toJSONSchema(schema, {
7580
- io: "input",
7581
- unrepresentable: "any"
7582
- });
7583
- }
7584
- function optionsSchema(options) {
7585
- return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
7586
- }
7587
- function commandData(spec) {
7588
- return {
7589
- name: spec.commandLine,
7590
- description: spec.description,
7591
- help: spec.help ?? null,
7592
- effect: spec.effect,
7593
- operator: spec.operator,
7594
- examples: spec.examples,
7595
- args: inputSchema(spec.command.args ?? object({})),
7596
- options: optionsSchema(spec.options),
7597
- standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
7598
- usage,
7599
- description,
7600
- schema: inputSchema(schema)
7601
- })),
7602
- output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
7603
- };
7604
- }
7605
- function nodeData(node) {
7606
- return node.kind === "command" ? commandData(node) : groupData(node);
7607
- }
7608
- function groupData(group) {
7609
- return {
7610
- name: group.commandLine,
7611
- commands: Object.values(visibleCommands(group)).map(nodeData)
7612
- };
7613
- }
7614
- function printed$1(data) {
7615
- return `${JSON.stringify(data)}\n`;
7616
- }
7617
- function errorsData(errors) {
7618
- return Object.fromEntries(Object.entries(errors).map(([code, declaration]) => [code, {
7619
- class: declaration.class,
7620
- details: declaration.details === void 0 ? null : toJSONSchema(declaration.details, { unrepresentable: "any" })
7621
- }]));
7622
- }
7623
- function cliHelpJson({ version, errors }, root) {
7624
- return printed$1({
7625
- name: root.commandLine,
7626
- version,
7627
- ...nodeData(root),
7628
- errors: errorsData(errors ?? {})
7629
- });
7630
- }
7631
- function groupHelpJson(group) {
7632
- return printed$1(groupData(group));
7633
- }
7634
- function commandHelpJson(spec) {
7635
- return printed$1(commandData(spec));
7636
- }
7637
- //#endregion
7638
- //#region ../agent-cli/dist/output.js
7639
- const SUCCESS_EXIT_CODE = 0;
7640
- function withTrailingNewline(text) {
7641
- return text === "" || text.endsWith("\n") ? text : `${text}\n`;
7642
- }
7643
- function printed(value, render, { json, invocation, style, fields }) {
7644
- if (!(value instanceof Page)) return json ? `${JSON.stringify(value)}\n` : withTrailingNewline(render(style));
7645
- const page = printedPage(value, invocation);
7646
- if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
7647
- const rows = projected(page.items, fields);
7648
- return json ? `${JSON.stringify({
7649
- ...page,
7650
- items: rows
7651
- })}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
7652
- }
7653
- /** A progress message as stderr carries it: one JSON object per line under `--json` (ADR 0006). */
7654
- function progressLine(message, json) {
7655
- return json ? `${JSON.stringify({ progress: message })}\n` : `${message}\n`;
7656
- }
7657
- function errorText(error, format) {
7658
- const text = formatRuntimeError(error, {
7659
- ...format,
7660
- isBug: (each) => isBug(each, format.errors)
7661
- });
7662
- const { usageHelp, json } = format;
7663
- return usageHelp !== void 0 && !json && error instanceof UsageError ? `${text}\n${usageHelp}` : text;
7664
- }
7665
- function stdoutOf({ value, render }, format, spill) {
7666
- const text = printed(value, render, format);
7667
- return value instanceof Page ? text : spill(text, format.json);
7668
- }
7669
- /** What a serve Command prints the moment it starts. */
7670
- function startedOutput(outcome, format, spill) {
7671
- return stdoutOf(outcome, {
7672
- ...format,
7673
- fields: void 0
7674
- }, spill);
7675
- }
7676
- /** A serve Command stopped after it started: stopping it is how it is meant to end. */
7677
- const STOPPED_RESULT = {
7678
- stdout: "",
7679
- stderr: "",
7680
- exitCode: SUCCESS_EXIT_CODE
7681
- };
7682
- function errorResult(error, format) {
7683
- return {
7684
- stdout: "",
7685
- stderr: errorText(error, format),
7686
- exitCode: exitCodeOf(error, format.errors, format.stop)
7687
- };
7688
- }
7689
- async function commandResult(settled, format, spill) {
7690
- if (settled.ok) try {
7691
- return {
7692
- stdout: await stdoutOf(settled.outcome, format, spill),
7693
- stderr: "",
7694
- exitCode: SUCCESS_EXIT_CODE
7695
- };
7696
- } catch (spillError) {
7697
- return errorResult(spillError, format);
7698
- }
7699
- const { error, delivered } = settled;
7700
- const result = errorResult(error, format);
7701
- if (delivered === void 0) return result;
7702
- try {
7703
- return {
7704
- ...result,
7705
- stdout: await stdoutOf(delivered, format, spill)
7706
- };
7707
- } catch (spillError) {
7708
- const both = new AggregateError([...errorsOf(error), spillError]);
7709
- return {
7710
- ...result,
7711
- stderr: errorText(both, format)
7712
- };
7713
- }
7714
- }
7715
- function textHelp(node, root, cli, style) {
7716
- if (node === root) return cliHelp(cli, node, style);
7717
- return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
7718
- }
7719
- function helpOf(node, root, cli, style, json) {
7720
- if (!json) return textHelp(node, root, cli, style);
7721
- if (node === root) return cliHelpJson(cli, node);
7722
- return node.kind === "command" ? commandHelpJson(node) : groupHelpJson(node);
7723
- }
7724
- function helpResult(node, root, cli, { style, json }) {
7725
- return {
7726
- stdout: helpOf(node, root, cli, style, json),
7727
- stderr: "",
7728
- exitCode: SUCCESS_EXIT_CODE
7729
- };
8346
+ const EFFECT_NOTES = {
8347
+ read: "changes nothing",
8348
+ write: "changes something; show the Plan with --dry-run",
8349
+ destructive: "changes something hard to undo; show the Plan with --dry-run, run with --confirm",
8350
+ serve: "changes nothing; prints its data at once, then runs until Ctrl-C, which exits 0"
8351
+ };
8352
+ const OPERATOR_NOTE = "only a person at a terminal runs it; a Coding agent asks the operator to type it after `!`";
8353
+ function choicesOf(field) {
8354
+ return field.choices === void 0 ? [] : [`one of: ${field.choices.join(", ")}`];
7730
8355
  }
7731
- /** The first line of `--version`: the version, and the commit and time of a stamped build. */
7732
- function versionLine({ version, build }) {
7733
- return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
8356
+ function defaultsOf(field) {
8357
+ if (field.defaultValue === void 0) return [];
8358
+ if (field.type !== "boolean") return [`default: ${JSON.stringify(field.defaultValue)}`];
8359
+ return field.defaultValue === true ? ["on by default; this flag turns it off"] : [];
7734
8360
  }
7735
- function versionResult(cli, packageRoot) {
7736
- return {
7737
- stdout: `${(cli.build === void 0 ? [versionLine(cli)] : [versionLine(cli), `changelog ${join(packageRoot, "CHANGELOG.md")}`]).join("\n")}\n`,
7738
- stderr: "",
7739
- exitCode: SUCCESS_EXIT_CODE
7740
- };
8361
+ function fieldRow(field, notes) {
8362
+ const all = [...field.required ? ["required"] : [], ...notes];
8363
+ const suffix = all.length === 0 ? "" : `(${all.join("; ")})`;
8364
+ return [field.usage, [field.description ?? "", suffix].filter((part) => part !== "").join(" ")];
7741
8365
  }
7742
- //#endregion
7743
- //#region ../agent-cli/dist/skill-source.js
7744
- /** The branch a release moves to the released commit, which holds the skill of that version. */
7745
- const RELEASE_BRANCH = "release";
7746
- const GITHUB_SHORTHAND = /^[\w.-]+\/[\w.-]+$/;
7747
- const GIT_URL_PROTOCOLS = /* @__PURE__ */ new Set([
7748
- "https:",
7749
- "http:",
7750
- "ssh:",
7751
- "git:"
7752
- ]);
7753
- const SCP_STYLE = /^[\w.-]+@[\w.-]+:[\w./-]+$/;
7754
- const manifestSchema = object({ repository: union([string(), object({ url: string() })]).optional() });
7755
- function sourceFromRepository(repository) {
7756
- if (GITHUB_SHORTHAND.test(repository)) return repository;
7757
- const url = repository.replace(/^git\+/, "");
7758
- if (!(URL.canParse(url) && GIT_URL_PROTOCOLS.has(new URL(url).protocol)) && !SCP_STYLE.test(url)) return void 0;
7759
- return `${url.endsWith(".git") ? url : `${url}.git`}#${RELEASE_BRANCH}`;
8366
+ function argRow(field) {
8367
+ return fieldRow(field, [...choicesOf(field), ...defaultsOf(field)]);
7760
8368
  }
7761
- async function repositoryAt(packageRoot) {
7762
- let manifest;
7763
- try {
7764
- manifest = JSON.parse(await readFile(join(packageRoot, "package.json"), "utf8"));
7765
- } catch {
7766
- return;
7767
- }
7768
- const parsed = manifestSchema.safeParse(manifest);
7769
- const repository = parsed.success ? parsed.data.repository : void 0;
7770
- return typeof repository === "object" ? repository.url : repository;
8369
+ function optionRow(field) {
8370
+ return fieldRow(field, defaultsOf(field));
7771
8371
  }
7772
- /** What `npx skills add` installs the CLI's skill from: the declared source, else the package's repository. */
7773
- async function skillSource(declared, packageRoot) {
7774
- if (declared !== void 0) return declared.source;
7775
- const repository = await repositoryAt(packageRoot);
7776
- return repository === void 0 ? void 0 : sourceFromRepository(repository);
8372
+ function flagRow({ usage, description }) {
8373
+ return [usage, description];
7777
8374
  }
7778
- //#endregion
7779
- //#region ../agent-cli/dist/skills.js
7780
- /** Where the skills CLI (`npx skills add`) puts a skill, under a project and under home. */
7781
- const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7782
- function skillUpdateCommand(cliName) {
7783
- return `npx -y skills update ${cliName}`;
8375
+ function commandLines(spec, style, { examples }) {
8376
+ return [
8377
+ style.bold(spec.commandLine),
8378
+ spec.description,
8379
+ ...examples ? examplesSection(spec.examples, style) : [],
8380
+ ...section("Arguments", spec.args.map(argRow), style),
8381
+ ...section("Options", spec.options.map(optionRow), style),
8382
+ ...spec.help === void 0 ? [] : ["", spec.help],
8383
+ "",
8384
+ `${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`,
8385
+ ...spec.operator ? [`${style.bold("Operator Command:")} ${OPERATOR_NOTE}`] : [],
8386
+ ...section("Standard flags", spec.standardFlags.map(flagRow), style)
8387
+ ];
7784
8388
  }
7785
- function skillAddCommand(source, cliName) {
7786
- return `npx -y skills add ${source} --skill ${cliName} -g`;
8389
+ function commandHelp(spec, style) {
8390
+ return lines(...commandLines(spec, style, { examples: true }));
7787
8391
  }
7788
- const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
7789
- const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
7790
- /** The version a SKILL.md's frontmatter `metadata.version` states. */
7791
- function skillVersionIn(skill) {
7792
- const frontmatter = FRONTMATTER.exec(skill)?.[1];
7793
- return frontmatter === void 0 ? void 0 : METADATA_VERSION.exec(`${frontmatter}\n`)?.[1];
8392
+ //#endregion
8393
+ //#region ../agent-cli/dist/help-json.js
8394
+ function inputSchema(schema) {
8395
+ return toJSONSchema(schema, {
8396
+ io: "input",
8397
+ unrepresentable: "any"
8398
+ });
7794
8399
  }
7795
- function homeOf(env) {
7796
- return env.HOME ?? env.USERPROFILE ?? homedir();
8400
+ function optionsSchema(options) {
8401
+ return inputSchema(object(Object.fromEntries(options.map(({ name, schema }) => [name, schema]))));
7797
8402
  }
7798
- async function copyAt(path, cliVersion) {
7799
- let skill;
7800
- try {
7801
- skill = await readFile(join(path, "SKILL.md"), "utf8");
7802
- } catch (error) {
7803
- if (isMissingFile(error)) return;
7804
- return {
7805
- path,
7806
- version: void 0,
7807
- state: "unreadable"
7808
- };
7809
- }
7810
- const version = skillVersionIn(skill);
7811
- if (version === void 0 || !isVersion(version) || !isVersion(cliVersion)) return {
7812
- path,
7813
- version,
7814
- state: "unversioned"
7815
- };
8403
+ function commandData(spec) {
7816
8404
  return {
7817
- path,
7818
- version,
7819
- state: standingOf(version, cliVersion)
8405
+ name: spec.commandLine,
8406
+ description: spec.description,
8407
+ help: spec.help ?? null,
8408
+ effect: spec.effect,
8409
+ operator: spec.operator,
8410
+ examples: spec.examples,
8411
+ args: inputSchema(spec.command.args ?? object({})),
8412
+ options: optionsSchema(spec.options),
8413
+ standardFlags: spec.standardFlags.map(({ usage, description, schema }) => ({
8414
+ usage,
8415
+ description,
8416
+ schema: inputSchema(schema)
8417
+ })),
8418
+ output: spec.output === void 0 ? null : toJSONSchema(spec.output, { unrepresentable: "any" })
7820
8419
  };
7821
8420
  }
7822
- /** Every installed copy of the skill named after the CLI, in the project first, then under home. */
7823
- async function skillCopies(skillName, cliVersion, cwd, env) {
7824
- const paths = [cwd, homeOf(env)].flatMap((root) => SKILL_DIRS.map((dir) => join(root, ...dir, skillName)));
7825
- const seen = /* @__PURE__ */ new Set();
7826
- const copies = [];
7827
- for (const path of paths) {
7828
- const copy = await copyAt(path, cliVersion);
7829
- const real = copy === void 0 ? void 0 : await realpath(path);
7830
- if (copy !== void 0 && real !== void 0 && !seen.has(real)) {
7831
- seen.add(real);
7832
- copies.push(copy);
7833
- }
7834
- }
7835
- return copies;
8421
+ function nodeData(node) {
8422
+ return node.kind === "command" ? commandData(node) : groupData(node);
7836
8423
  }
7837
- //#endregion
7838
- //#region ../agent-cli/dist/doctor-rows.js
7839
- function ok(message) {
8424
+ function groupData(group) {
7840
8425
  return {
7841
- state: "ok",
7842
- message
8426
+ name: group.commandLine,
8427
+ commands: Object.values(visibleCommands(group)).map(nodeData)
7843
8428
  };
7844
8429
  }
7845
- function buildRow(cli) {
7846
- return {
7847
- name: "build",
7848
- run: () => ok(versionLine(cli))
7849
- };
8430
+ function printed$1(data) {
8431
+ return `${JSON.stringify(data)}\n`;
7850
8432
  }
7851
- function nodeRow(cli) {
7852
- return {
7853
- name: "node",
7854
- run: () => {
7855
- const found = process.versions.node;
7856
- const range = cli.engines?.node;
7857
- if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
7858
- const minimum = engineMinimum(range);
7859
- if (minimum === void 0) return {
7860
- state: "problem",
7861
- message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
7862
- };
7863
- if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
7864
- const major = minimum[0];
7865
- return {
7866
- state: "problem",
7867
- message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
7868
- fix: `nvm install ${major} && nvm alias default ${major}`
7869
- };
7870
- }
7871
- };
8433
+ function errorsData(errors) {
8434
+ return Object.fromEntries(Object.entries(errors).map(([code, declaration]) => [code, {
8435
+ class: declaration.class,
8436
+ details: declaration.details === void 0 ? null : toJSONSchema(declaration.details, { unrepresentable: "any" })
8437
+ }]));
7872
8438
  }
7873
- function projectRow(projectFile) {
7874
- const { file } = projectFile;
7875
- return {
7876
- name: "project",
7877
- run: (ctx) => {
7878
- let project;
7879
- try {
7880
- project = projectOf(projectFile, ctx.cwd);
7881
- } catch (error) {
7882
- if (error instanceof CliError && error.code === "PROJECT_INVALID") return {
7883
- state: "problem",
7884
- message: error.message
7885
- };
7886
- throw error;
7887
- }
7888
- return ok(project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(project.root, file));
7889
- }
7890
- };
8439
+ function cliHelpJson({ version, description, errors }, root) {
8440
+ return printed$1({
8441
+ name: root.commandLine,
8442
+ version,
8443
+ description,
8444
+ ...nodeData(root),
8445
+ errors: errorsData(errors ?? {})
8446
+ });
7891
8447
  }
7892
- function installRow(cli, { bin, node }) {
7893
- return {
7894
- name: "install",
7895
- advisory: true,
7896
- run: async (ctx) => {
7897
- const { install } = cli;
7898
- if (install === void 0) return ok(`${cli.name} declares no install, so it has no \`update\``);
7899
- try {
7900
- return ok(`a global npm install under ${(await globalInstallOf(bin, npmContext(cli, install, ctx, node))).prefix}`);
7901
- } catch (error) {
7902
- if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
7903
- state: "problem",
7904
- message: error.message,
7905
- fix: installCommandLine(install)
7906
- };
7907
- throw error;
7908
- }
7909
- }
7910
- };
8448
+ function groupHelpJson(group) {
8449
+ return printed$1(groupData(group));
7911
8450
  }
7912
- function registryRow(cli, node) {
7913
- return {
7914
- name: "registry",
7915
- advisory: true,
7916
- run: async (ctx) => {
7917
- const { install } = cli;
7918
- if (install === void 0) return ok(`${cli.name} declares no registry to compare this build with`);
7919
- const latest = await latestVersion(npmContext(cli, install, ctx, node));
7920
- const standing = standingOf(cli.version, latest);
7921
- const head = `${install.registry} has ${latest} as latest: this build`;
7922
- if (standing === "behind") return {
7923
- state: "problem",
7924
- message: `${head} (${cli.version}) is older`,
7925
- fix: `${cli.name} update`
7926
- };
7927
- return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
7928
- }
7929
- };
8451
+ function commandHelpJson(spec) {
8452
+ return printed$1(commandData(spec));
7930
8453
  }
7931
- const SKILL_STATE_NOTES = {
7932
- current: "the same as this build",
7933
- behind: "older than this build",
7934
- ahead: "newer than this build",
7935
- unversioned: "states no metadata.version",
7936
- unreadable: "could not be read"
7937
- };
7938
- function describeCopy({ path, version, state }) {
7939
- return `${path}${version === void 0 ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
8454
+ //#endregion
8455
+ //#region ../agent-cli/dist/output.js
8456
+ const SUCCESS_EXIT_CODE = 0;
8457
+ function withTrailingNewline(text) {
8458
+ return text === "" || text.endsWith("\n") ? text : `${text}\n`;
8459
+ }
8460
+ function printed(value, render, { json, invocation, style, fields }) {
8461
+ if (!(value instanceof Page)) return json ? `${JSON.stringify(value)}\n` : withTrailingNewline(render(style));
8462
+ const page = printedPage(value, invocation);
8463
+ if (fields === void 0) return json ? `${JSON.stringify(page)}\n` : `${withTrailingNewline(render(style))}${pageFooter(page)}`;
8464
+ const rows = projected(page.items, fields);
8465
+ return json ? `${JSON.stringify({
8466
+ ...page,
8467
+ items: rows
8468
+ })}\n` : `${renderRows(rows, fields)}${pageFooter(page)}`;
7940
8469
  }
7941
- function skillFix(cli, copies) {
7942
- if (copies.some(({ state }) => state === "behind" || state === "unversioned")) return skillUpdateCommand(cli.name);
7943
- return copies.some(({ state }) => state === "ahead") ? `${cli.name} update` : void 0;
8470
+ /** A progress message as stderr carries it: one JSON object per line under `--json` (ADR 0006). */
8471
+ function progressLine(message, json) {
8472
+ return json ? `${JSON.stringify({ progress: message })}\n` : `${message}\n`;
7944
8473
  }
7945
- function skillRow(cli) {
7946
- return {
7947
- name: "skill",
7948
- run: async (ctx) => {
7949
- const copies = await skillCopies(cli.name, cli.version, ctx.cwd, ctx.env);
7950
- if (copies.length === 0) {
7951
- const message = `no copy of the ${cli.name} skill is installed`;
7952
- const source = await skillSource(cli.skill, ctx.packageRoot);
7953
- return source === void 0 ? ok(message) : {
7954
- state: "ok",
7955
- message,
7956
- fix: skillAddCommand(source, cli.name)
7957
- };
7958
- }
7959
- const fix = skillFix(cli, copies);
7960
- const message = copies.map(describeCopy).join("; ");
7961
- return fix === void 0 ? ok(message) : {
7962
- state: "ok",
7963
- message,
7964
- fix
7965
- };
7966
- }
7967
- };
8474
+ function errorText(error, format) {
8475
+ const text = formatRuntimeError(error, {
8476
+ ...format,
8477
+ isBug: (each) => isBug(each, format.errors)
8478
+ });
8479
+ const { usageHelp, json } = format;
8480
+ return usageHelp !== void 0 && !json && error instanceof UsageError ? `${text}\n${usageHelp}` : text;
7968
8481
  }
7969
- /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
7970
- function doctorChecks(cli, launch) {
7971
- return [
7972
- buildRow(cli),
7973
- nodeRow(cli),
7974
- ...cli.project === void 0 ? [] : [projectRow(cli.project)],
7975
- ...cli.doctor ?? [],
7976
- ...toolRows(cli.tools ?? [], cli.commands),
7977
- installRow(cli, launch),
7978
- registryRow(cli, launch.node),
7979
- skillRow(cli)
7980
- ];
8482
+ function stdoutOf({ value, render }, format, spill) {
8483
+ const text = printed(value, render, format);
8484
+ return value instanceof Page ? text : spill(text, format.json);
7981
8485
  }
7982
- //#endregion
7983
- //#region ../agent-cli/dist/doctor.js
7984
- const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
7985
- const DEFAULT_DEADLINE_MS = 1e4;
7986
- const MS_PER_SECOND = 1e3;
7987
- const MIN_LABEL_WIDTH = 8;
7988
- const reportSchema = object({
7989
- rows: array(object({
7990
- check: string(),
7991
- state: _enum([
7992
- "ok",
7993
- "problem",
7994
- "skipped"
7995
- ]),
7996
- message: string(),
7997
- fix: string().optional(),
7998
- code: string().optional()
7999
- })),
8000
- next: array(string())
8001
- });
8002
- function problemOf(check, error, { signal, deadline, ms }) {
8003
- if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
8004
- state: "problem",
8005
- message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
8006
- };
8007
- if (error instanceof CliError) return {
8008
- state: "problem",
8009
- message: `${error.message}; ${error.hint}`,
8010
- code: error.code
8011
- };
8486
+ /** What a serve Command prints the moment it starts. */
8487
+ function startedOutput(outcome, format, spill) {
8488
+ return stdoutOf(outcome, {
8489
+ ...format,
8490
+ fields: void 0
8491
+ }, spill);
8492
+ }
8493
+ /** A serve Command stopped after it started: stopping it is how it is meant to end. */
8494
+ const STOPPED_RESULT = {
8495
+ stdout: "",
8496
+ stderr: "",
8497
+ exitCode: SUCCESS_EXIT_CODE
8498
+ };
8499
+ function errorResult(error, format) {
8012
8500
  return {
8013
- state: "problem",
8014
- message: `the ${check.name} row hit a bug: ${messageOf(error)}`
8501
+ stdout: "",
8502
+ stderr: errorText(error, format),
8503
+ exitCode: exitCodeOf(error, format.errors, format.stop)
8015
8504
  };
8016
8505
  }
8017
- function withSignal(ctx, signal) {
8018
- return Object.create(ctx, { signal: { value: signal } });
8019
- }
8020
- async function rowOf(check, ctx) {
8021
- const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
8022
- const signal = ctx.deadline(ms);
8023
- let finding;
8506
+ async function commandResult(settled, format, spill) {
8507
+ if (settled.ok) try {
8508
+ return {
8509
+ stdout: await stdoutOf(settled.outcome, format, spill),
8510
+ stderr: "",
8511
+ exitCode: SUCCESS_EXIT_CODE
8512
+ };
8513
+ } catch (spillError) {
8514
+ return errorResult(spillError, format);
8515
+ }
8516
+ const { error, delivered } = settled;
8517
+ const result = errorResult(error, format);
8518
+ if (delivered === void 0) return result;
8024
8519
  try {
8025
- finding = await Promise.race([Promise.resolve().then(() => check.run(withSignal(ctx, signal))), untilAborted(signal)]);
8026
- } catch (error) {
8027
- if (ctx.signal.aborted) return void 0;
8028
- finding = problemOf(check, error, {
8029
- signal: ctx.signal,
8030
- deadline: signal,
8031
- ms
8032
- });
8520
+ return {
8521
+ ...result,
8522
+ stdout: await stdoutOf(delivered, format, spill)
8523
+ };
8524
+ } catch (spillError) {
8525
+ const both = new AggregateError([...errorsOf(error), spillError]);
8526
+ return {
8527
+ ...result,
8528
+ stderr: errorText(both, format)
8529
+ };
8033
8530
  }
8034
- return {
8035
- row: {
8036
- check: check.name,
8037
- ...finding
8038
- },
8039
- advisory: check.advisory === true
8040
- };
8041
8531
  }
8042
- function nextOf(rows) {
8043
- return [...new Set(rows.flatMap(({ fix }) => fix === void 0 ? [] : [fix]))];
8532
+ function textHelp(node, root, cli, style) {
8533
+ if (node === root) return cliHelp(cli, node, style);
8534
+ return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
8044
8535
  }
8045
- function render({ rows, next }, style) {
8046
- const width = Math.max(MIN_LABEL_WIDTH, ...rows.map(({ check }) => check.length)) + 2;
8047
- const stateWidth = Math.max(...rows.map(({ state }) => state.length)) + 2;
8048
- return [...rows.map(({ check, state, message }) => {
8049
- const label = state === "problem" ? style.red(state) : state;
8050
- return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
8051
- }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
8536
+ function helpOf(node, root, cli, style, json) {
8537
+ if (!json) return textHelp(node, root, cli, style);
8538
+ if (node === root) return cliHelpJson(cli, node);
8539
+ return node.kind === "command" ? commandHelpJson(node) : groupHelpJson(node);
8052
8540
  }
8053
- function failure(cliName, problems) {
8054
- const errors = problems.map(({ check, message, fix, code }) => new CliError({
8055
- code: code ?? "DOCTOR_PROBLEMS",
8056
- message: `${cliName} doctor found a problem in ${check}: ${message}`,
8057
- hint: fix === void 0 ? "the row says what is wrong" : `run ${fix}, then run doctor again`
8058
- }));
8059
- return new AggregateError(errors);
8541
+ function helpResult(node, root, cli, { style, json }) {
8542
+ return {
8543
+ stdout: helpOf(node, root, cli, style, json),
8544
+ stderr: "",
8545
+ exitCode: SUCCESS_EXIT_CODE
8546
+ };
8060
8547
  }
8061
- /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
8062
- function doctorCommand(cli, launch) {
8063
- return defineCommand$1({
8064
- name: "doctor",
8065
- description: "check the setup and say what fixes each problem",
8066
- examples: [{
8067
- command: `${cli.name} doctor`,
8068
- note: "check the setup and list the commands that fix it"
8069
- }, {
8070
- command: `${cli.name} doctor --json`,
8071
- note: "get the rows and fixes as data"
8072
- }],
8073
- effect: "read",
8074
- output: reportSchema,
8075
- run: async (_input, ctx) => {
8076
- const checked = (await Promise.all(doctorChecks(cli, launch).map((check) => rowOf(check, ctx)))).filter((row) => row !== void 0);
8077
- const rows = checked.map(({ row }) => row);
8078
- const report = {
8079
- rows,
8080
- next: nextOf(rows)
8081
- };
8082
- if (ctx.signal.aborted) {
8083
- ctx.deliver(report);
8084
- ctx.signal.throwIfAborted();
8085
- }
8086
- const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
8087
- if (problems.length > 0) {
8088
- ctx.deliver(report);
8089
- throw failure(cli.name, problems);
8090
- }
8091
- return report;
8092
- },
8093
- render
8094
- });
8548
+ function versionResult(cli, packageRoot) {
8549
+ return {
8550
+ stdout: `${(cli.build === void 0 ? [versionLine(cli)] : [versionLine(cli), `changelog ${join(packageRoot, "CHANGELOG.md")}`]).join("\n")}\n`,
8551
+ stderr: "",
8552
+ exitCode: SUCCESS_EXIT_CODE
8553
+ };
8095
8554
  }
8096
8555
  //#endregion
8097
- //#region ../agent-cli/dist/upgrade-notes.js
8098
- const RELEASE_HEADING = /^## v?(\S+)/;
8099
- function releasesIn(changelog) {
8100
- const releases = [];
8101
- let current;
8102
- for (const line of changelog.split(/\r?\n/)) {
8103
- const heading = RELEASE_HEADING.exec(line);
8104
- if (heading !== null) {
8105
- const version = heading[1] ?? "";
8106
- current = isVersion(version) ? {
8107
- version,
8108
- lines: []
8109
- } : void 0;
8110
- if (current !== void 0) releases.push(current);
8111
- } else current?.lines.push(line);
8112
- }
8113
- return releases.map(({ version, lines }) => ({
8114
- version,
8115
- notes: lines.join("\n").trim()
8116
- }));
8117
- }
8556
+ //#region ../agent-cli/dist/package-root.js
8118
8557
  /**
8119
- * What changed in each release after `from` up to `to`, oldest first, from the CHANGELOG.md in
8120
- * `packageDir`; none when the package ships no CHANGELOG.
8558
+ * The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
8559
+ * from `cwd`; the search's start when none holds one. The bin is resolved through links first,
8560
+ * since npm's global bin is a link to it.
8121
8561
  */
8122
- async function upgradeNotes(packageDir, from, to) {
8123
- let changelog;
8124
- try {
8125
- changelog = await readFile(join(packageDir, "CHANGELOG.md"), "utf8");
8126
- } catch (error) {
8127
- if (isMissingFile(error)) return [];
8128
- throw error;
8562
+ function packageRootOf(bin, cwd) {
8563
+ const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
8564
+ for (let dir = start;; dir = dirname(dir)) {
8565
+ if (existsSync(join(dir, "package.json"))) return dir;
8566
+ if (dirname(dir) === dir) return start;
8129
8567
  }
8130
- return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version));
8131
8568
  }
8132
8569
  //#endregion
8133
- //#region ../agent-cli/dist/update.js
8134
- const updateSchema = object({
8135
- from: string(),
8136
- latest: string(),
8137
- installed: object({
8138
- version: string(),
8139
- prefix: string()
8140
- }).nullable(),
8141
- upgrades: array(object({
8142
- version: string(),
8143
- notes: string()
8144
- })),
8145
- next: array(string())
8146
- });
8147
- function nothingToInstall({ from, latest }) {
8148
- return standingOf(from, latest) === "current" ? `already on ${from}, the latest release: nothing to install` : `${from} is newer than the latest release ${latest}: nothing to install`;
8570
+ //#region ../agent-cli/dist/suggest.js
8571
+ const MAX_DISTANCE = 3;
8572
+ const MIN_SIMILARITY = .4;
8573
+ function distance(a, b) {
8574
+ const width = b.length + 1;
8575
+ const d = Array.from({ length: (a.length + 1) * width }, (_, k) => {
8576
+ const i = Math.floor(k / width);
8577
+ const j = k % width;
8578
+ return i === 0 || j === 0 ? i + j : 0;
8579
+ });
8580
+ const at = (i, j) => d[i * width + j] ?? 0;
8581
+ for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
8582
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
8583
+ const edit = Math.min(at(i - 1, j) + 1, at(i, j - 1) + 1, at(i - 1, j - 1) + cost);
8584
+ const transposed = i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1];
8585
+ d[i * width + j] = transposed ? Math.min(edit, at(i - 2, j - 2) + 1) : edit;
8586
+ }
8587
+ return at(a.length, b.length);
8149
8588
  }
8150
- function renderUpdate(cli, install, update) {
8151
- const { installed } = update;
8152
- if (installed === null) return nothingToInstall(update);
8153
- return [
8154
- `installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`,
8155
- ...update.upgrades.map(({ version, notes }) => `\nchanges in ${version}:\n${notes}`),
8156
- "",
8157
- ...update.next.map((command) => `next: ${command}, so the ${cli.name} skill describes ${installed.version}`)
8158
- ].join("\n");
8589
+ function isClose(word, candidate) {
8590
+ const length = Math.max(word.length, candidate.length);
8591
+ const edits = distance(word, candidate);
8592
+ return candidate.length > 1 && edits < MAX_DISTANCE && (length - edits) / length > MIN_SIMILARITY;
8159
8593
  }
8160
- function interruptedAfterInstall(plan) {
8161
- return new CliError({
8162
- code: INTERRUPTED,
8163
- message: `interrupted, but ${plan.package}@${plan.latest} had already been installed with the npm under ${plan.prefix}, replacing ${plan.from}`,
8164
- hint: "nothing to run again: run the commands under next"
8165
- });
8594
+ /** The names closest to `word`, sorted, several when they tie, or none when nothing is close. */
8595
+ function closestNames(word, names) {
8596
+ const close = [...new Set(names)].filter((candidate) => isClose(word, candidate));
8597
+ const best = Math.min(...close.map((candidate) => distance(word, candidate)));
8598
+ return close.filter((candidate) => distance(word, candidate) === best).sort((a, b) => a.localeCompare(b));
8166
8599
  }
8167
- /** The built-in `update`, for a CLI installed globally with npm. */
8168
- function updateCommand(cli, install, { bin, node }) {
8169
- return defineCommand$1({
8170
- name: "update",
8171
- description: "install the latest version of this CLI from its registry",
8172
- examples: [{
8173
- command: `${cli.name} update`,
8174
- note: "install the latest version and show what changed"
8175
- }, {
8176
- command: `${cli.name} update --dry-run`,
8177
- note: "show the version it would install"
8178
- }],
8179
- effect: "write",
8180
- output: updateSchema,
8181
- plan: async (_input, ctx) => {
8182
- const npm = npmContext(cli, install, ctx, node);
8183
- const copy = await globalInstallOf(bin, npm);
8184
- return {
8185
- package: install.package,
8186
- from: cli.version,
8187
- latest: await latestVersion(npm),
8188
- prefix: copy.prefix,
8189
- packageDir: copy.packageDir
8190
- };
8191
- },
8192
- apply: async (plan, ctx) => {
8193
- const { from, latest, prefix } = plan;
8194
- if (standingOf(from, latest) !== "behind") return {
8195
- from,
8196
- latest,
8197
- installed: null,
8198
- upgrades: [],
8199
- next: []
8200
- };
8201
- ctx.signal.throwIfAborted();
8202
- ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
8203
- await ctx.runToEnd(`installing ${plan.package}@${latest}`, () => installGlobally(latest, npmContext(cli, install, ctx, node)));
8204
- const update = {
8205
- from,
8206
- latest,
8207
- installed: {
8208
- version: latest,
8209
- prefix
8210
- },
8211
- upgrades: await upgradeNotes(plan.packageDir, from, latest),
8212
- next: [skillUpdateCommand(cli.name)]
8213
- };
8214
- if (ctx.signal.aborted) {
8215
- ctx.deliver(update);
8216
- throw interruptedAfterInstall(plan);
8217
- }
8218
- return update;
8219
- },
8220
- render: (update) => renderUpdate(cli, install, update),
8221
- renderPlan: (plan) => standingOf(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan)
8222
- });
8600
+ /** The long flags closest to `flag`, sorted, or none for a short flag or when nothing is close. */
8601
+ function closestFlags(flag, flags) {
8602
+ if (!flag.startsWith("--")) return [];
8603
+ const names = flags.map((candidate) => candidate.slice(2));
8604
+ return closestNames(flag.slice(2), names).map((name) => `--${name}`);
8223
8605
  }
8224
- //#endregion
8225
- //#region ../agent-cli/dist/built-ins.js
8226
- /** The Commands the Runtime adds to a multi-Command CLI: `update` only when it declares an install. */
8227
- function builtInCommands(cli, launch) {
8228
- return {
8229
- doctor: doctorCommand(cli, launch),
8230
- ...cli.install === void 0 ? {} : { update: updateCommand(cli, cli.install, launch) }
8231
- };
8606
+ /** The one long flag closest to `flag`, or none when nothing is close or several tie. */
8607
+ function closestFlag(flag, flags) {
8608
+ const closest = closestFlags(flag, flags);
8609
+ return closest.length === 1 ? closest[0] : void 0;
8232
8610
  }
8233
8611
  //#endregion
8234
- //#region ../agent-cli/dist/agent-cli.js
8235
- function checkOwnCodes(cli) {
8236
- const redeclared = runtimeCodesIn(cli.errors ?? {});
8237
- if (redeclared.length > 0) throw new Error(`${cli.name} declares ${redeclared.join(", ")}, an error code the Runtime already declares`);
8612
+ //#region ../agent-cli/dist/route.js
8613
+ const GROUP_FLAGS = [
8614
+ HELP_FLAG,
8615
+ VERSION_FLAG,
8616
+ JSON_FLAG.flag
8617
+ ];
8618
+ function beforeEnd(words) {
8619
+ const end = words.indexOf("--");
8620
+ return end === -1 ? words : words.slice(0, end);
8238
8621
  }
8239
- function defineCli$1(cli) {
8240
- checkOwnCodes(cli);
8241
- if (cli.commands === void 0) return cli;
8242
- const { commands } = cli;
8243
- const taken = Object.keys(builtInCommands(cli, {
8244
- bin: void 0,
8245
- node: void 0
8246
- })).filter((name) => commands[name] !== void 0);
8247
- if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
8248
- return cli;
8622
+ function wantsHelp(words) {
8623
+ return beforeEnd(words).some((word) => word === "--help" || word === "-h");
8249
8624
  }
8250
- /**
8251
- * Declares the CLI's ports and its Project file's settings once; the `defineCommand` and
8252
- * `defineCli` it returns type `ctx.ports` and `ctx.project`.
8253
- */
8254
- function agentCli() {
8255
- return {
8256
- defineCommand: defineCommandFor(),
8257
- defineCli: defineCli$1
8625
+ function childOf(group, name) {
8626
+ return Object.hasOwn(group.commands, name) ? group.commands[name] : void 0;
8627
+ }
8628
+ function isFlagLike(word) {
8629
+ return word.length > 1 && word[0] === "-";
8630
+ }
8631
+ function walk(node, words, json, invocation) {
8632
+ const help = {
8633
+ kind: "help",
8634
+ node,
8635
+ json
8636
+ };
8637
+ if (node.kind === "command") return wantsHelp(words) ? help : {
8638
+ kind: "command",
8639
+ spec: node,
8640
+ words,
8641
+ json
8258
8642
  };
8643
+ const [first, ...rest] = words;
8644
+ if (first === void 0) return help;
8645
+ const child = childOf(node, first);
8646
+ if (child !== void 0) return walk(child, rest, json, invocation);
8647
+ if (first === "--") {
8648
+ const [name, ...after] = rest;
8649
+ if (name === void 0) return help;
8650
+ const named = childOf(node, name);
8651
+ if (named !== void 0) return walk(named, ["--", ...after], json, invocation);
8652
+ return unknownCommand(node, name, json);
8653
+ }
8654
+ if (wantsHelp(words)) return help;
8655
+ if (isFlagLike(first)) {
8656
+ const suggestions = closestFlags(first, GROUP_FLAGS);
8657
+ return {
8658
+ kind: "usage error",
8659
+ error: unknownOptionError(node.commandLine, invocation, first, suggestions),
8660
+ node,
8661
+ json
8662
+ };
8663
+ }
8664
+ return unknownCommand(node, first, json);
8259
8665
  }
8260
- //#endregion
8261
- //#region ../agent-cli/dist/error-table.js
8262
- /** Keeps each code's literal class and `details` schema, so `cliError` can type its throw sites. */
8263
- function defineErrors(table) {
8264
- return table;
8666
+ function unknownCommand(group, name, json) {
8667
+ const suggestions = closestNames(name, Object.keys(visibleCommands(group)));
8668
+ return {
8669
+ kind: "usage error",
8670
+ error: unknownCommandError(group.commandLine, name, suggestions),
8671
+ node: group,
8672
+ json
8673
+ };
8265
8674
  }
8266
- /** A `CliError` for a code of `table`, whose `details` must match the code's schema. */
8267
- function cliError(_table, code, init, options) {
8268
- return new CliError({
8269
- ...init,
8270
- code
8271
- }, options);
8675
+ /** Walks the caller's command line through the Commands to the one it names, or to a stop. */
8676
+ function route(root, invocation) {
8677
+ const { argv } = invocation;
8678
+ const options = beforeEnd(argv);
8679
+ const json = options.includes(JSON_FLAG.flag);
8680
+ if (options.includes("--version")) return {
8681
+ kind: "version",
8682
+ json
8683
+ };
8684
+ return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
8272
8685
  }
8273
8686
  //#endregion
8274
8687
  //#region ../agent-cli/dist/execute.js
@@ -8341,67 +8754,12 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, invoca
8341
8754
  }, outputs);
8342
8755
  if (!ctx.force) guardOutputs(outputs, ctx.commandLine({ add: [FORCE_FLAG.flag] }));
8343
8756
  }
8344
- return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
8345
- }
8346
- //#endregion
8347
- //#region ../agent-cli/dist/package-root.js
8348
- /**
8349
- * The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
8350
- * from `cwd`; the search's start when none holds one. The bin is resolved through links first,
8351
- * since npm's global bin is a link to it.
8352
- */
8353
- function packageRootOf(bin, cwd) {
8354
- const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
8355
- for (let dir = start;; dir = dirname(dir)) {
8356
- if (existsSync(join(dir, "package.json"))) return dir;
8357
- if (dirname(dir) === dir) return start;
8358
- }
8359
- }
8360
- //#endregion
8361
- //#region ../agent-cli/dist/suggest.js
8362
- const MAX_DISTANCE = 3;
8363
- const MIN_SIMILARITY = .4;
8364
- function distance(a, b) {
8365
- const width = b.length + 1;
8366
- const d = Array.from({ length: (a.length + 1) * width }, (_, k) => {
8367
- const i = Math.floor(k / width);
8368
- const j = k % width;
8369
- return i === 0 || j === 0 ? i + j : 0;
8370
- });
8371
- const at = (i, j) => d[i * width + j] ?? 0;
8372
- for (let i = 1; i <= a.length; i++) for (let j = 1; j <= b.length; j++) {
8373
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
8374
- const edit = Math.min(at(i - 1, j) + 1, at(i, j - 1) + 1, at(i - 1, j - 1) + cost);
8375
- const transposed = i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1];
8376
- d[i * width + j] = transposed ? Math.min(edit, at(i - 2, j - 2) + 1) : edit;
8377
- }
8378
- return at(a.length, b.length);
8379
- }
8380
- function isClose(word, candidate) {
8381
- const length = Math.max(word.length, candidate.length);
8382
- const edits = distance(word, candidate);
8383
- return candidate.length > 1 && edits < MAX_DISTANCE && (length - edits) / length > MIN_SIMILARITY;
8384
- }
8385
- /** The names closest to `word`, sorted, several when they tie, or none when nothing is close. */
8386
- function closestNames(word, names) {
8387
- const close = [...new Set(names)].filter((candidate) => isClose(word, candidate));
8388
- const best = Math.min(...close.map((candidate) => distance(word, candidate)));
8389
- return close.filter((candidate) => distance(word, candidate) === best).sort((a, b) => a.localeCompare(b));
8390
- }
8391
- /** The long flags closest to `flag`, sorted, or none for a short flag or when nothing is close. */
8392
- function closestFlags(flag, flags) {
8393
- if (!flag.startsWith("--")) return [];
8394
- const names = flags.map((candidate) => candidate.slice(2));
8395
- return closestNames(flag.slice(2), names).map((name) => `--${name}`);
8396
- }
8397
- /** The one long flag closest to `flag`, or none when nothing is close or several tie. */
8398
- function closestFlag(flag, flags) {
8399
- const closest = closestFlags(flag, flags);
8400
- return closest.length === 1 ? closest[0] : void 0;
8757
+ return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
8401
8758
  }
8402
8759
  //#endregion
8403
8760
  //#region ../agent-cli/dist/parse-input.js
8404
8761
  const NO_INPUT = object({});
8762
+ const WHITESPACE = /\s/;
8405
8763
  function knownFlags(spec) {
8406
8764
  return [...spec.options.map((field) => ({
8407
8765
  flag: field.label,
@@ -8538,6 +8896,8 @@ async function parseInput(spec, words, invocation, readStdin) {
8538
8896
  page: page.data,
8539
8897
  values
8540
8898
  };
8899
+ const [onlyWord] = unknownOptions;
8900
+ 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);
8541
8901
  const [only] = unknown;
8542
8902
  if (only !== void 0 && unknown.length === 1 && otherLines.length === 0) throw unknownOptionError(spec.commandLine, invocation, only.flag, only.suggestion === void 0 ? [] : [only.suggestion]);
8543
8903
  throw invalidInput(spec.commandLine, invocation, unknown, otherLines);
@@ -8563,83 +8923,9 @@ async function parseCommand({ spec, words, json }, invocation, readStdin) {
8563
8923
  };
8564
8924
  }
8565
8925
  //#endregion
8566
- //#region ../agent-cli/dist/route.js
8567
- const GROUP_FLAGS = [
8568
- HELP_FLAG,
8569
- VERSION_FLAG,
8570
- JSON_FLAG.flag
8571
- ];
8572
- function beforeEnd(words) {
8573
- const end = words.indexOf("--");
8574
- return end === -1 ? words : words.slice(0, end);
8575
- }
8576
- function wantsHelp(words) {
8577
- return beforeEnd(words).some((word) => word === "--help" || word === "-h");
8578
- }
8579
- function childOf(group, name) {
8580
- return Object.hasOwn(group.commands, name) ? group.commands[name] : void 0;
8581
- }
8582
- function isFlagLike(word) {
8583
- return word.length > 1 && word[0] === "-";
8584
- }
8585
- function walk(node, words, json, invocation) {
8586
- const help = {
8587
- kind: "help",
8588
- node,
8589
- json
8590
- };
8591
- if (node.kind === "command") return wantsHelp(words) ? help : {
8592
- kind: "command",
8593
- spec: node,
8594
- words,
8595
- json
8596
- };
8597
- const [first, ...rest] = words;
8598
- if (first === void 0) return help;
8599
- const child = childOf(node, first);
8600
- if (child !== void 0) return walk(child, rest, json, invocation);
8601
- if (first === "--") {
8602
- const [name, ...after] = rest;
8603
- if (name === void 0) return help;
8604
- const named = childOf(node, name);
8605
- if (named !== void 0) return walk(named, ["--", ...after], json, invocation);
8606
- return unknownCommand(node, name, json);
8607
- }
8608
- if (wantsHelp(words)) return help;
8609
- if (isFlagLike(first)) {
8610
- const suggestions = closestFlags(first, GROUP_FLAGS);
8611
- return {
8612
- kind: "usage error",
8613
- error: unknownOptionError(node.commandLine, invocation, first, suggestions),
8614
- node,
8615
- json
8616
- };
8617
- }
8618
- return unknownCommand(node, first, json);
8619
- }
8620
- function unknownCommand(group, name, json) {
8621
- const suggestions = closestNames(name, Object.keys(visibleCommands(group)));
8622
- return {
8623
- kind: "usage error",
8624
- error: unknownCommandError(group.commandLine, name, suggestions),
8625
- node: group,
8626
- json
8627
- };
8628
- }
8629
- /** Walks the caller's command line through the Commands to the one it names, or to a stop. */
8630
- function route(root, invocation) {
8631
- const { argv } = invocation;
8632
- const options = beforeEnd(argv);
8633
- const json = options.includes(JSON_FLAG.flag);
8634
- if (options.includes("--version")) return {
8635
- kind: "version",
8636
- json
8637
- };
8638
- return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
8639
- }
8640
- //#endregion
8641
8926
  //#region ../agent-cli/dist/secrets.js
8642
- function dotenvPath(cliName, env) {
8927
+ /** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
8928
+ function envFileOf(cliName, env) {
8643
8929
  const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
8644
8930
  return join(config, cliName, ".env");
8645
8931
  }
@@ -8656,24 +8942,50 @@ function readDotenv(path, fileError) {
8656
8942
  }
8657
8943
  return parseEnv(text);
8658
8944
  }
8659
- /** Looks `name` up in the env, then in `$XDG_CONFIG_HOME/<cli>/.env`; an empty value counts as unset. */
8660
- function secretOf({ cliName, env, fileError }, name) {
8945
+ /** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
8946
+ function secretOf({ envFile, env, fileError }, name) {
8661
8947
  const fromEnv = env[name];
8662
8948
  if (fromEnv) return {
8663
8949
  value: fromEnv,
8664
8950
  origin: { kind: "env" }
8665
8951
  };
8666
- const path = dotenvPath(cliName, env);
8667
- const fromFile = readDotenv(path, fileError)[name];
8952
+ const fromFile = readDotenv(envFile, fileError)[name];
8668
8953
  return fromFile ? {
8669
8954
  value: fromFile,
8670
8955
  origin: {
8671
8956
  kind: "file",
8672
- path
8957
+ path: envFile
8673
8958
  }
8674
8959
  } : void 0;
8675
8960
  }
8676
8961
  //#endregion
8962
+ //#region ../agent-cli/dist/side-task.js
8963
+ /** How long a run waits for its Side task once its output is written. */
8964
+ const SIDE_TASK_WAIT_MS = 1e3;
8965
+ const noSideTask = async () => {};
8966
+ function waitOver() {
8967
+ return new DOMException(`the side task ran past its ${SIDE_TASK_WAIT_MS} ms wait`, "TimeoutError");
8968
+ }
8969
+ function startSideTask(sideTask, runWith, { clock, failed }) {
8970
+ if (sideTask === void 0) return noSideTask;
8971
+ const controller = new AbortController();
8972
+ const { signal } = controller;
8973
+ const running = (async () => sideTask(runWith(signal)))().catch((error) => {
8974
+ if (!(signal.aborted && error === signal.reason)) failed(error);
8975
+ });
8976
+ return async () => {
8977
+ const waited = new AbortController();
8978
+ const capped = new Promise((resolve) => after(clock, SIDE_TASK_WAIT_MS, waited.signal, () => {
8979
+ const over = waitOver();
8980
+ failed(over);
8981
+ controller.abort(over);
8982
+ resolve();
8983
+ }));
8984
+ await Promise.race([running, capped]);
8985
+ waited.abort();
8986
+ };
8987
+ }
8988
+ //#endregion
8677
8989
  //#region ../agent-cli/dist/spill.js
8678
8990
  const STDOUT_BUDGET_CHARS = 3e4;
8679
8991
  const SPILL_RETENTION_MS = 6048e5;
@@ -8726,47 +9038,91 @@ function spillTo(target) {
8726
9038
  };
8727
9039
  }
8728
9040
  //#endregion
8729
- //#region ../agent-cli/dist/style.js
8730
- function colourEnabled(env, stdoutIsTTY) {
8731
- if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
8732
- return env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0" || stdoutIsTTY && !isAgent(env);
9041
+ //#region ../agent-cli/dist/run-command.js
9042
+ function stdinNeverEndedHint(callerLine) {
9043
+ return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
8733
9044
  }
8734
- function sgr(open, close) {
8735
- return (text) => `\u001b[${open}m${text}\u001b[${close}m`;
9045
+ function selfOf(name, bin) {
9046
+ return bin === void 0 ? [name] : [
9047
+ process.execPath,
9048
+ ...process.execArgv,
9049
+ bin
9050
+ ];
8736
9051
  }
8737
- const COLOURED = {
8738
- bold: sgr(1, 22),
8739
- dim: sgr(2, 22),
8740
- red: sgr(31, 39),
8741
- green: sgr(32, 39),
8742
- yellow: sgr(33, 39),
8743
- cyan: sgr(36, 39)
8744
- };
8745
- const plain = (text) => text;
8746
- const PLAIN = {
8747
- bold: plain,
8748
- dim: plain,
8749
- red: plain,
8750
- green: plain,
8751
- yellow: plain,
8752
- cyan: plain
8753
- };
8754
- function styleFor(env, stdoutIsTTY) {
8755
- return colourEnabled(env, stdoutIsTTY) ? COLOURED : PLAIN;
9052
+ function portsOf(cli, replacement) {
9053
+ if (replacement !== void 0) return () => replacement;
9054
+ return cli.ports ?? (() => ({}));
8756
9055
  }
8757
- //#endregion
8758
- //#region ../agent-cli/dist/run-cli.js
8759
- function stdinNeverEndedHint(callerLine) {
8760
- return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
9056
+ function portsBaseOf(scope, signal) {
9057
+ const { cli, options, packageRoot, progress, fileError, clock, files } = scope;
9058
+ const { cwd, env } = options;
9059
+ const envFile = envFileOf(cli.name, env);
9060
+ return {
9061
+ cwd,
9062
+ env,
9063
+ packageRoot,
9064
+ progress,
9065
+ secret: (name) => secretOf({
9066
+ envFile,
9067
+ env,
9068
+ fileError
9069
+ }, name),
9070
+ envFile,
9071
+ fileError,
9072
+ version: cli.version,
9073
+ build: cli.build,
9074
+ self: selfOf(cli.name, options.bin),
9075
+ signal,
9076
+ clock,
9077
+ files,
9078
+ spawn: contextSpawn(options.spawn ?? realSpawn, {
9079
+ cwd,
9080
+ env,
9081
+ signal,
9082
+ progress
9083
+ }, fileError)
9084
+ };
9085
+ }
9086
+ function contextOf(portsBase, control, parts) {
9087
+ const { ports, project, force, commandLine, deliver } = parts;
9088
+ return {
9089
+ ...portsBase,
9090
+ deadline: (ms) => control.deadline(ms),
9091
+ timeout: (ms) => control.timeout(ms),
9092
+ runToEnd: (name, work) => control.runToEnd(name, work),
9093
+ get ports() {
9094
+ return ports();
9095
+ },
9096
+ project,
9097
+ force,
9098
+ commandLine,
9099
+ poll: (check, { intervalMs, signal: stop }) => poll(portsBase.clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
9100
+ deliver
9101
+ };
8761
9102
  }
8762
- async function runCommand(routed, { base, writeStdout, signal, format: baseFormat, ports, clock, files, spawn, projectFile, stdin, platform }) {
8763
- const { command, commandLine } = routed.spec;
9103
+ /** Leaves the Side task running, so the caller writes the result before it waits for it. */
9104
+ async function runCommand(cli, routed, options, write, baseFormat) {
9105
+ const { command, commandLine, builtIn } = routed.spec;
9106
+ const { cwd, env } = options;
9107
+ const clock = options.clock ?? realClock;
9108
+ const files = options.files ?? realFiles();
9109
+ const stdin = options.stdin ?? "";
9110
+ const platform = options.platform ?? process.platform;
9111
+ const ports = portsOf(cli, options.ports);
9112
+ const packageRoot = packageRootOf(options.bin, cwd);
9113
+ const fileError = fileErrorOf({
9114
+ callerLine: commandLineOf(baseFormat.invocation),
9115
+ env,
9116
+ platform
9117
+ });
9118
+ const progress = (message) => write.stderr(progressLine(message, routed.json));
8764
9119
  const invocation = {
8765
9120
  ...baseFormat.invocation,
8766
9121
  flags: knownFlags(routed.spec)
8767
9122
  };
8768
9123
  const format = {
8769
9124
  ...baseFormat,
9125
+ bugs: () => manifestAt(packageRoot).bugs,
8770
9126
  json: routed.json,
8771
9127
  invocation
8772
9128
  };
@@ -8774,71 +9130,91 @@ async function runCommand(routed, { base, writeStdout, signal, format: baseForma
8774
9130
  let resumeHint;
8775
9131
  const control = new RunControl({
8776
9132
  commandLine,
8777
- outer: signal,
9133
+ outer: options.signal,
8778
9134
  clock,
8779
- progress: base.progress,
9135
+ progress,
8780
9136
  runDeadline: command.deadline === void 0 ? void 0 : {
8781
9137
  ms: command.deadline.ms,
8782
9138
  hint: () => resumeHint ?? stdinNeverEndedHint(typedLine())
8783
9139
  }
8784
9140
  });
9141
+ const failed = (error, finishSideTask) => {
9142
+ control.close();
9143
+ return {
9144
+ result: errorResult(error, format),
9145
+ finishSideTask
9146
+ };
9147
+ };
8785
9148
  let parsed;
8786
- let project;
8787
9149
  try {
8788
- checkOperator(routed.spec, base.env, typedLine());
9150
+ checkOperator(routed.spec, env, typedLine());
8789
9151
  parsed = await parseCommand(routed, invocation, (field) => {
8790
- if (isTerminal(stdin)) base.progress(readingLine(field.name, platform));
8791
- return readStdin(stdin, control.signal);
9152
+ if (isTerminal(stdin)) progress(readingLine(field.name, platform));
9153
+ return readStdin(stdin, control.signal, {
9154
+ clock,
9155
+ stalled: () => stdinStalled(typedLine())
9156
+ });
8792
9157
  });
8793
- project = projectFile === void 0 ? void 0 : projectOf(projectFile, base.cwd);
8794
9158
  } catch (error) {
8795
- control.close();
8796
- return errorResult(error, format);
9159
+ return failed(error, noSideTask);
8797
9160
  }
8798
9161
  const { input, flags } = parsed;
8799
9162
  resumeHint = command.deadline?.resume(input, typedLine);
8800
- let delivered;
8801
- const { fileError } = base;
8802
9163
  const outputs = outputsGuard(classifiedFiles(files, {
8803
- cwd: base.cwd,
8804
- fileError
8805
- }), base.cwd);
8806
- const spill = spillTo({
8807
- cliName: format.cliName,
8808
- commandName: command.name,
8809
- env: base.env,
8810
- clock,
8811
- files: outputs.files,
9164
+ cwd,
8812
9165
  fileError
8813
- });
8814
- const portsBase = {
8815
- ...base,
8816
- signal: control.signal,
9166
+ }), cwd);
9167
+ const scope = {
9168
+ cli,
9169
+ options,
9170
+ packageRoot,
9171
+ progress,
9172
+ fileError,
8817
9173
  clock,
8818
- files: outputs.files,
8819
- spawn: contextSpawn(spawn, {
8820
- cwd: base.cwd,
8821
- env: base.env,
8822
- signal: control.signal,
8823
- progress: base.progress
8824
- }, fileError)
9174
+ files: outputs.files
8825
9175
  };
8826
- const builtPorts = once(() => ports(portsBase));
8827
- const ctx = {
8828
- ...portsBase,
8829
- deadline: (ms) => control.deadline(ms),
8830
- runToEnd: (name, work) => control.runToEnd(name, work),
8831
- get ports() {
8832
- return builtPorts();
8833
- },
9176
+ const portsBase = portsBaseOf(scope, control.signal);
9177
+ const finishSideTask = startSideTask(builtIn ? void 0 : cli.sideTask?.bind(cli), (sideSignal) => {
9178
+ const sideBase = portsBaseOf(scope, sideSignal);
9179
+ const sidePorts = once(() => ports(sideBase));
9180
+ return {
9181
+ ...sideBase,
9182
+ command: routed.spec.path,
9183
+ dryRun: flags.dryRun,
9184
+ get ports() {
9185
+ return sidePorts();
9186
+ }
9187
+ };
9188
+ }, {
9189
+ clock,
9190
+ failed: (error) => {
9191
+ if (isDebug(baseFormat)) progress(`side task failed: ${traceOf(error)}`);
9192
+ }
9193
+ });
9194
+ let project;
9195
+ try {
9196
+ project = builtIn || cli.project === void 0 ? void 0 : projectOf(cli.project, cwd, fileError);
9197
+ } catch (error) {
9198
+ return failed(error, finishSideTask);
9199
+ }
9200
+ let delivered;
9201
+ const ctx = contextOf(portsBase, control, {
9202
+ ports: once(() => ports(portsBase)),
8834
9203
  project,
8835
9204
  force: flags.force,
8836
9205
  commandLine: typedLine,
8837
- poll: (check, { intervalMs, signal: stop }) => poll(clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
8838
9206
  deliver: (data) => {
8839
9207
  delivered = deliveredOutcome(command, data);
8840
9208
  }
8841
- };
9209
+ });
9210
+ const spill = spillTo({
9211
+ cliName: format.cliName,
9212
+ commandName: command.name,
9213
+ env,
9214
+ clock,
9215
+ files: outputs.files,
9216
+ fileError
9217
+ });
8842
9218
  let served = false;
8843
9219
  let settled;
8844
9220
  try {
@@ -8852,7 +9228,7 @@ async function runCommand(routed, { base, writeStdout, signal, format: baseForma
8852
9228
  invocation,
8853
9229
  commandLine,
8854
9230
  started: async (outcome) => {
8855
- writeStdout(await startedOutput(outcome, format, spill));
9231
+ write.stdout(await startedOutput(outcome, format, spill));
8856
9232
  served = true;
8857
9233
  },
8858
9234
  files,
@@ -8860,17 +9236,23 @@ async function runCommand(routed, { base, writeStdout, signal, format: baseForma
8860
9236
  }))
8861
9237
  };
8862
9238
  } catch (error) {
8863
- if (served && control.signal.aborted) return STOPPED_RESULT;
9239
+ if (served && control.signal.aborted) return {
9240
+ result: STOPPED_RESULT,
9241
+ finishSideTask
9242
+ };
8864
9243
  settled = {
8865
9244
  ok: false,
8866
9245
  error: control.errorAfter(error),
8867
9246
  delivered
8868
9247
  };
8869
9248
  }
8870
- return commandResult(settled, {
8871
- ...format,
8872
- fields: flags.fields
8873
- }, spill);
9249
+ return {
9250
+ result: await commandResult(settled, {
9251
+ ...format,
9252
+ fields: flags.fields
9253
+ }, spill),
9254
+ finishSideTask
9255
+ };
8874
9256
  }
8875
9257
  /** `build` runs on the first call; later calls give its value or throw its error again. */
8876
9258
  function once(build) {
@@ -8891,17 +9273,37 @@ function once(build) {
8891
9273
  throw outcome.error;
8892
9274
  };
8893
9275
  }
8894
- function selfOf(name, bin) {
8895
- return bin === void 0 ? [name] : [
8896
- process.execPath,
8897
- ...process.execArgv,
8898
- bin
8899
- ];
9276
+ //#endregion
9277
+ //#region ../agent-cli/dist/style.js
9278
+ function colourEnabled(env, stdoutIsTTY) {
9279
+ if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
9280
+ return env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0" || stdoutIsTTY && !isAgent(env);
8900
9281
  }
8901
- function portsOf(cli, replacement) {
8902
- if (replacement !== void 0) return () => replacement;
8903
- return cli.ports ?? (() => ({}));
9282
+ function sgr(open, close) {
9283
+ return (text) => `\u001b[${open}m${text}\u001b[${close}m`;
9284
+ }
9285
+ const COLOURED = {
9286
+ bold: sgr(1, 22),
9287
+ dim: sgr(2, 22),
9288
+ red: sgr(31, 39),
9289
+ green: sgr(32, 39),
9290
+ yellow: sgr(33, 39),
9291
+ cyan: sgr(36, 39)
9292
+ };
9293
+ const plain = (text) => text;
9294
+ const PLAIN = {
9295
+ bold: plain,
9296
+ dim: plain,
9297
+ red: plain,
9298
+ green: plain,
9299
+ yellow: plain,
9300
+ cyan: plain
9301
+ };
9302
+ function styleFor(env, stdoutIsTTY) {
9303
+ return colourEnabled(env, stdoutIsTTY) ? COLOURED : PLAIN;
8904
9304
  }
9305
+ //#endregion
9306
+ //#region ../agent-cli/dist/run-cli.js
8905
9307
  function stopLine(notice, json) {
8906
9308
  return json ? progressLine(notice, true) : `\n${notice}\n`;
8907
9309
  }
@@ -8915,7 +9317,14 @@ async function whileStopping(signal, onStop, work) {
8915
9317
  signal.removeEventListener("abort", onStop);
8916
9318
  }
8917
9319
  }
9320
+ /** Runs the CLI, writes its output, then waits for the Side task it started. */
8918
9321
  async function runWithProgress(cli, argv, options, write, stopNotice) {
9322
+ const { result, finishSideTask } = await routeAndRun(cli, argv, options, write, stopNotice);
9323
+ await write.result(result);
9324
+ await finishSideTask();
9325
+ return result;
9326
+ }
9327
+ async function routeAndRun(cli, argv, options, write, stopNotice) {
8919
9328
  const format = {
8920
9329
  cliName: cli.name,
8921
9330
  env: options.env,
@@ -8929,64 +9338,37 @@ async function runWithProgress(cli, argv, options, write, stopNotice) {
8929
9338
  errors: cli.errors ?? {},
8930
9339
  stop: options.signal
8931
9340
  };
8932
- const builtIns = cli.commands === void 0 ? {} : builtInCommands(cli, {
9341
+ const root = cli.commands === void 0 ? {
9342
+ ...commandSpec(cli.name, cli.command),
9343
+ description: cli.description
9344
+ } : groupSpec(cli.name, cli.commands, builtInCommands(cli, {
8933
9345
  bin: options.bin,
8934
9346
  node: options.node
8935
- });
8936
- const root = cli.commands === void 0 ? commandSpec(cli.name, cli.command) : groupSpec(cli.name, {
8937
- ...cli.commands,
8938
- ...builtIns
8939
- });
9347
+ }));
8940
9348
  const routed = route(root, format.invocation);
8941
- const platform = options.platform ?? process.platform;
8942
- const fileError = fileErrorOf({
8943
- callerLine: commandLineOf(format.invocation),
8944
- env: options.env,
8945
- platform
8946
- });
8947
9349
  const misused = (node) => isAgent(options.env) ? {
8948
9350
  ...format,
8949
9351
  usageHelp: textHelp(node, root, cli, format.style)
8950
9352
  } : format;
8951
9353
  switch (routed.kind) {
8952
- case "version": return versionResult(cli, packageRootOf(options.bin, options.cwd));
8953
- case "help": return helpResult(routed.node, root, cli, {
9354
+ case "version": return ranAlone(versionResult(cli, packageRootOf(options.bin, options.cwd)));
9355
+ case "help": return ranAlone(helpResult(routed.node, root, cli, {
8954
9356
  ...format,
8955
9357
  json: routed.json
8956
- });
8957
- case "usage error": return errorResult(routed.error, {
9358
+ }));
9359
+ case "usage error": return ranAlone(errorResult(routed.error, {
8958
9360
  ...misused(routed.node),
8959
9361
  json: routed.json
8960
- });
8961
- case "command": return whileStopping(options.signal, stopNotice === void 0 ? void 0 : () => write.stderr(stopLine(stopNotice, routed.json)), () => runCommand(routed, {
8962
- base: {
8963
- cwd: options.cwd,
8964
- env: options.env,
8965
- packageRoot: packageRootOf(options.bin, options.cwd),
8966
- progress: (message) => write.stderr(progressLine(message, routed.json)),
8967
- secret: (name) => secretOf({
8968
- cliName: cli.name,
8969
- env: options.env,
8970
- fileError
8971
- }, name),
8972
- fileError,
8973
- version: cli.version,
8974
- build: cli.build,
8975
- self: selfOf(cli.name, options.bin)
8976
- },
8977
- writeStdout: (text) => write.stdout(text),
8978
- signal: options.signal,
8979
- format: misused(routed.spec),
8980
- ports: portsOf(cli, options.ports),
8981
- clock: options.clock ?? realClock,
8982
- files: options.files ?? realFiles(),
8983
- spawn: options.spawn ?? realSpawn,
8984
- projectFile: Object.values(builtIns).includes(routed.spec.command) ? void 0 : cli.project,
8985
- stdin: options.stdin ?? "",
8986
- platform
8987
9362
  }));
9363
+ case "command": return whileStopping(options.signal, stopNotice === void 0 ? void 0 : () => write.stderr(stopLine(stopNotice, routed.json)), () => runCommand(cli, routed, options, write, misused(routed.spec)));
8988
9364
  }
8989
9365
  }
9366
+ async function ranAlone(result) {
9367
+ return {
9368
+ result: await result,
9369
+ finishSideTask: noSideTask
9370
+ };
9371
+ }
8990
9372
  //#endregion
8991
9373
  //#region ../agent-cli/dist/run-process.js
8992
9374
  function written(stream, text) {
@@ -9011,10 +9393,12 @@ async function runProcess(cli) {
9011
9393
  stdin: process.stdin
9012
9394
  }, {
9013
9395
  stdout: (text) => process.stdout.write(text),
9014
- stderr: (text) => process.stderr.write(text)
9396
+ stderr: (text) => process.stderr.write(text),
9397
+ result: async ({ stdout, stderr }) => {
9398
+ await written(process.stdout, stdout);
9399
+ await written(process.stderr, stderr);
9400
+ }
9015
9401
  }, "stopping… (Ctrl-C again to quit now)");
9016
- await written(process.stdout, result.stdout);
9017
- await written(process.stderr, result.stderr);
9018
9402
  process.exitCode = result.exitCode;
9019
9403
  } finally {
9020
9404
  process.off("SIGINT", onSignal);
@@ -9023,11 +9407,15 @@ async function runProcess(cli) {
9023
9407
  process.exit();
9024
9408
  }
9025
9409
  //#endregion
9410
+ //#region package.json
9411
+ var version$1 = "0.7.0";
9412
+ var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
9413
+ //#endregion
9026
9414
  //#region src/build.ts
9027
9415
  const build = {
9028
- "sha": "c325cae",
9416
+ "sha": "10574f5",
9029
9417
  "dirty": false,
9030
- "builtAt": "2026-10-19T14:33:32.022Z"
9418
+ "builtAt": "2026-10-20T03:42:41.670Z"
9031
9419
  };
9032
9420
  //#endregion
9033
9421
  //#region src/steps.ts
@@ -9156,11 +9544,13 @@ async function writeTemplate(root, files, values) {
9156
9544
  }
9157
9545
  //#endregion
9158
9546
  //#region src/version.ts
9159
- const version = "0.5.0";
9547
+ const version = version$1;
9160
9548
  //#endregion
9161
9549
  //#region src/create.ts
9162
9550
  const { defineCommand } = agentCli();
9163
9551
  const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?([a-z0-9][a-z0-9._-]*)$/;
9552
+ const RELEASE_BRANCH = "release";
9553
+ const NO_REPOSITORY = "<!-- Set `repository` in package.json, then put here the `! npx -y skills add` line that `node dist/bin.js doctor` gives as the `skill` row's fix. -->";
9164
9554
  function errorCode(error) {
9165
9555
  return error instanceof Error && "code" in error ? error.code : void 0;
9166
9556
  }
@@ -9173,6 +9563,17 @@ async function isFree(path) {
9173
9563
  throw error;
9174
9564
  }
9175
9565
  }
9566
+ function skillInstallLine(repository, binName) {
9567
+ if (repository === void 0) return NO_REPOSITORY;
9568
+ return `\`! npx -y skills add ${`${repository.endsWith(".git") ? repository : `${repository}.git`}#${RELEASE_BRANCH}`} --skill ${binName} -g\``;
9569
+ }
9570
+ const MANIFEST = record(string(), unknown());
9571
+ async function setRepository(root, repository) {
9572
+ const path = join(root, "package.json");
9573
+ const manifest = MANIFEST.parse(JSON.parse(await readFile(path, "utf8")));
9574
+ const entries = Object.entries(manifest).flatMap((entry) => entry[0] === "description" ? [entry, ["repository", repository]] : [entry]);
9575
+ await writeFile(path, `${JSON.stringify(Object.fromEntries(entries), null, 2)}\n`);
9576
+ }
9176
9577
  async function ensureFree(path) {
9177
9578
  if (!await isFree(path)) throw new UsageError({
9178
9579
  code: "TARGET_NOT_EMPTY",
@@ -9192,9 +9593,10 @@ const create = defineCommand({
9192
9593
  options: object({
9193
9594
  description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
9194
9595
  install: boolean().default(true).describe("run pnpm install"),
9195
- git: boolean().default(true).describe("create a git repository with an initial commit")
9596
+ git: boolean().default(true).describe("create a git repository with an initial commit"),
9597
+ repository: url({ protocol: /^https$/ }).optional().describe("the https URL of the CLI's git repository, where its skill is installed from")
9196
9598
  }),
9197
- plan: async ({ name, description, install, git }, ctx) => {
9599
+ plan: async ({ name, description, install, git, repository }, ctx) => {
9198
9600
  const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
9199
9601
  const path = resolve(ctx.cwd, binName);
9200
9602
  await ensureFree(path);
@@ -9203,7 +9605,8 @@ const create = defineCommand({
9203
9605
  binName,
9204
9606
  description,
9205
9607
  debugVariable: debugVariable(binName),
9206
- runtimeVersion: version
9608
+ runtimeVersion: version,
9609
+ skillInstall: skillInstallLine(repository, binName)
9207
9610
  };
9208
9611
  return {
9209
9612
  path,
@@ -9213,12 +9616,14 @@ const create = defineCommand({
9213
9616
  install,
9214
9617
  git
9215
9618
  }),
9216
- install
9619
+ install,
9620
+ repository
9217
9621
  };
9218
9622
  },
9219
9623
  apply: async (plan, ctx) => {
9220
9624
  await mkdir(plan.path, { recursive: true });
9221
9625
  await writeTemplate(plan.path, plan.files, plan.values);
9626
+ if (plan.repository !== void 0) await setRepository(plan.path, plan.repository);
9222
9627
  await runSteps(plan.steps, plan.path, ctx);
9223
9628
  return {
9224
9629
  path: plan.path,
@@ -9240,6 +9645,7 @@ const { defineCli } = agentCli();
9240
9645
  await runProcess(defineCli({
9241
9646
  name: "create-agent-cli",
9242
9647
  version,
9648
+ description,
9243
9649
  build,
9244
9650
  errors: stepErrors,
9245
9651
  command: create