@duyquangnvx/create-agent-cli 0.26.0 → 0.28.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
@@ -4,7 +4,7 @@ import { spawn } from "node:child_process";
4
4
  import { constants, homedir, tmpdir } from "node:os";
5
5
  import { parseArgs, parseEnv } from "node:util";
6
6
  import { constants as constants$1, existsSync, readFileSync, realpathSync } from "node:fs";
7
- import { access, chmod, link, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
7
+ import { access, chmod, link, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
8
8
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
9
9
  import { fileURLToPath } from "node:url";
10
10
  //#region ../../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v4/core/util.js
@@ -5947,15 +5947,6 @@ function errnoOf(error) {
5947
5947
  function isMissingFile(error) {
5948
5948
  return errnoOf(error) === "ENOENT";
5949
5949
  }
5950
- /** What `pending` resolves with, or undefined when it fails because a file is missing. */
5951
- async function orMissing(pending) {
5952
- try {
5953
- return await pending;
5954
- } catch (error) {
5955
- if (isMissingFile(error)) return void 0;
5956
- throw error;
5957
- }
5958
- }
5959
5950
  function isNothingThere(error) {
5960
5951
  return isMissingFile(error) || errnoOf(error) === "ENOTDIR";
5961
5952
  }
@@ -5981,14 +5972,14 @@ function bugHint(format) {
5981
5972
  const rerun = `rerun with ${debugVariable(format.cliName)}=1 to see the stack trace`;
5982
5973
  return bugs === void 0 ? rerun : `${rerun}, then report it at ${bugs}`;
5983
5974
  }
5984
- function fieldsOf$1(error, format) {
5975
+ function fieldsOf$1({ error, exitClass }, format) {
5985
5976
  if (!(error instanceof CliError)) return {
5986
5977
  code: INTERNAL,
5987
5978
  message: messageOf(error),
5988
5979
  hint: bugHint(format)
5989
5980
  };
5990
5981
  const { code, message, hint, details, retryAt, next } = error;
5991
- if (format.isBug(error)) return {
5982
+ if (exitClass === "bug") return {
5992
5983
  code,
5993
5984
  message,
5994
5985
  hint: bugHint(format),
@@ -6012,9 +6003,9 @@ function traceOf(error) {
6012
6003
  return error instanceof Error ? error.stack ?? error.message : String(error);
6013
6004
  }
6014
6005
  /** Under debug: a bug's stack trace, then every error's cause chain. */
6015
- function debugTrace(error, format) {
6006
+ function debugTrace({ error, exitClass }, format) {
6016
6007
  if (!isDebug(format)) return void 0;
6017
- const lines = [...format.isBug(error) ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
6008
+ const lines = [...exitClass === "bug" ? [traceOf(error)] : [], ...causesOf(error).map((cause) => `caused by: ${traceOf(cause)}`)];
6018
6009
  return lines.length === 0 ? void 0 : lines.join("\n");
6019
6010
  }
6020
6011
  /**
@@ -6033,29 +6024,29 @@ function printedDetail(error, format) {
6033
6024
  const value = Reflect.get(details, key);
6034
6025
  return typeof value === "string" ? value : void 0;
6035
6026
  }
6036
- function labelOf(error, { isBug, isWait, style }) {
6037
- if (isWait(error)) return style.yellow("wait:");
6038
- return style.red(isBug(error) ? "internal error:" : "error:");
6027
+ function labelOf({ exitClass }, style) {
6028
+ if (exitClass === "wait") return style.yellow("wait:");
6029
+ return style.red(exitClass === "bug" ? "internal error:" : "error:");
6039
6030
  }
6040
- function errorLines(error, format) {
6041
- const fields = fieldsOf$1(error, format);
6042
- const blocks = [printedDetail(error, format), debugTrace(error, format)].filter((block) => block !== void 0);
6031
+ function errorLines(leaf, format) {
6032
+ const fields = fieldsOf$1(leaf, format);
6033
+ const blocks = [printedDetail(leaf.error, format), debugTrace(leaf, format)].filter((block) => block !== void 0);
6043
6034
  const { style } = format;
6044
- const label = labelOf(error, format);
6035
+ const label = labelOf(leaf, style);
6045
6036
  const between = blocks.map((block) => `${block}\n`).join("");
6046
6037
  const runs = (fields.next ?? []).map((line) => `${style.cyan("run:")} ${line}\n`).join("");
6047
6038
  return `${label} ${fields.message}\n${between}${style.cyan("hint:")} ${fields.hint}\n${runs}`;
6048
6039
  }
6049
- function formatRuntimeError(error, format) {
6050
- const errors = errorsOf(error);
6040
+ /** The classified errors a run failed with, as stderr carries them. */
6041
+ function formatRuntimeError(leaves, format) {
6051
6042
  if (format.json) {
6052
- const printed = errors.map((each) => ({
6053
- ...fieldsOf$1(each, format),
6054
- stack: debugTrace(each, format)
6043
+ const printed = leaves.map((leaf) => ({
6044
+ ...fieldsOf$1(leaf, format),
6045
+ stack: debugTrace(leaf, format)
6055
6046
  }));
6056
6047
  return `${JSON.stringify({ errors: printed })}\n`;
6057
6048
  }
6058
- return errors.map((each) => errorLines(each, format)).join("");
6049
+ return leaves.map((leaf) => errorLines(leaf, format)).join("");
6059
6050
  }
6060
6051
  //#endregion
6061
6052
  //#region ../agent-cli/dist/tail.js
@@ -6085,8 +6076,8 @@ const WINDOWS_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
6085
6076
  function pathOf(env) {
6086
6077
  return env[Object.keys(env).find((name) => name.toUpperCase() === "PATH") ?? "PATH"] ?? "";
6087
6078
  }
6088
- function extensionsOf(env) {
6089
- return process.platform === "win32" ? (env.PATHEXT ?? WINDOWS_EXTENSIONS).split(";") : [""];
6079
+ function extensionsOf({ env, platform }) {
6080
+ return platform === "win32" ? (env.PATHEXT ?? WINDOWS_EXTENSIONS).split(";") : [""];
6090
6081
  }
6091
6082
  async function isExecutable(file) {
6092
6083
  try {
@@ -6098,19 +6089,19 @@ async function isExecutable(file) {
6098
6089
  }
6099
6090
  }
6100
6091
  /** The file names `name` may have on `PATH`: itself when it already ends in one of the extensions. */
6101
- function fileNamesOf(name, env) {
6102
- const extensions = extensionsOf(env);
6092
+ function fileNamesOf(name, search) {
6093
+ const extensions = extensionsOf(search);
6103
6094
  const own = extname(name).toUpperCase();
6104
6095
  return own !== "" && extensions.some((extension) => extension.toUpperCase() === own) ? [name] : extensions.map((extension) => `${name}${extension}`);
6105
6096
  }
6106
- /** Where `name` is found on `PATH` from `env`, or undefined. */
6107
- async function findTool(name, env) {
6108
- const candidates = pathOf(env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => fileNamesOf(name, env).map((file) => join(folder, file)));
6097
+ /** Where `name` is found on `PATH`, or undefined. */
6098
+ async function findTool(name, search) {
6099
+ const candidates = pathOf(search.env).split(delimiter).filter((folder) => folder !== "").flatMap((folder) => fileNamesOf(name, search).map((file) => join(folder, file)));
6109
6100
  for (const file of candidates) if (await isExecutable(file)) return file;
6110
6101
  }
6111
6102
  /** Fails with `TOOL_MISSING`, naming every missing tool at once, unless all are on `PATH`. */
6112
- async function checkTools(tools, env, rerun) {
6113
- const found = await Promise.all(tools.map((tool) => findTool(tool.name, env)));
6103
+ async function checkTools(tools, search, rerun) {
6104
+ const found = await Promise.all(tools.map((tool) => findTool(tool.name, search)));
6114
6105
  const missing = tools.filter((_tool, index) => found[index] === void 0);
6115
6106
  if (missing.length === 0) return;
6116
6107
  const names = missing.map(({ name }) => name).join(", ");
@@ -6122,11 +6113,14 @@ async function checkTools(tools, env, rerun) {
6122
6113
  });
6123
6114
  }
6124
6115
  /** One `doctor` row per distinct tool the CLI and its Commands declare, with its install command as the fix. */
6125
- function toolRows(tools, commands) {
6116
+ function toolRows(tools, commands, platform) {
6126
6117
  return declaredIn(tools, commands, (command) => command.tools).map(({ name, install }) => ({
6127
6118
  name,
6128
6119
  run: async (ctx) => {
6129
- const file = await findTool(name, ctx.env);
6120
+ const file = await findTool(name, {
6121
+ env: ctx.env,
6122
+ platform
6123
+ });
6130
6124
  return file === void 0 ? {
6131
6125
  state: "problem",
6132
6126
  message: `${name} is not on PATH`,
@@ -6164,7 +6158,10 @@ function batchCommandLine(command, file, args) {
6164
6158
  return [escapedForCmd(command), ...args.map((arg) => batchArgument(arg, shim))].join(" ");
6165
6159
  }
6166
6160
  async function windowsLaunch(command, args, env) {
6167
- const file = PATH_SEPARATOR.test(command) ? command : await findTool(command, env);
6161
+ const file = PATH_SEPARATOR.test(command) ? command : await findTool(command, {
6162
+ env,
6163
+ platform: process.platform
6164
+ });
6168
6165
  if (file === void 0 || !BATCH_FILE.test(file)) return {
6169
6166
  command,
6170
6167
  args,
@@ -6928,177 +6925,42 @@ function checkForce(command, label) {
6928
6925
  if (force.trim() === "") throw new Error(`Command '${label}' declares an empty force; say what ${FORCE_FLAG.flag} replaces`);
6929
6926
  }
6930
6927
  //#endregion
6931
- //#region ../agent-cli/dist/define-command.js
6932
- /** Throws when the Command's declaration breaks a rule its type cannot express; `label` names it. */
6933
- function checkCommand(command, label, table) {
6934
- checkNoStandardFlags(command, label);
6935
- checkForce(command, label);
6936
- checkArrayArgLast(label, command.args);
6937
- checkStdinFields(label, command.args, command.options);
6938
- checkNoBooleanArrayOption(label, command.options);
6939
- checkErrors(command, label, table);
6940
- }
6941
- function checkErrors(command, label, table) {
6942
- const unknown = (command.errors ?? []).filter((code) => !Object.hasOwn(table, code));
6943
- if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
6944
- }
6945
- function defineCommandFor() {
6946
- return declared;
6947
- }
6948
- function declared(command) {
6949
- return command;
6950
- }
6951
- const defineCommand$1 = defineCommandFor();
6952
- //#endregion
6953
- //#region ../agent-cli/dist/context/project.js
6954
- const PROJECT_INVALID = "PROJECT_INVALID";
6955
- function invalid(path, problem, rerun, options) {
6956
- return new CliError({
6957
- code: PROJECT_INVALID,
6958
- message: `the Project file ${path} is invalid: ${problem}`,
6959
- hint: `fix ${path}, then run the Next step`,
6960
- next: [rerun]
6961
- }, options);
6962
- }
6963
- function readText(path, fileError) {
6964
- try {
6965
- return readFileSync(path, "utf8");
6966
- } catch (error) {
6967
- if (isMissingFile(error)) return void 0;
6968
- throw fileError(error, {
6969
- path,
6970
- access: "read"
6971
- });
6972
- }
6973
- }
6974
- function parsed(path, text, schema, rerun) {
6975
- let json;
6976
- try {
6977
- json = JSON.parse(text);
6978
- } catch (error) {
6979
- throw invalid(path, messageOf(error), rerun, { cause: error });
6980
- }
6981
- const result = schema.safeParse(json);
6982
- if (!result.success) throw invalid(path, prettifyError(result.error), rerun);
6983
- return result.data;
6984
- }
6985
- function projectFrom(projectFile, folder, scope) {
6986
- const path = join(folder, projectFile.file);
6987
- const text = readText(path, scope.fileError);
6988
- if (text !== void 0) return {
6989
- root: folder,
6990
- settings: parsed(path, text, projectFile.schema, scope.rerun)
6928
+ //#region ../agent-cli/dist/context/confirm.js
6929
+ const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
6930
+ const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
6931
+ function checkConfirmation({ rerun, commandLine, given }, expected) {
6932
+ if (given === expected) return;
6933
+ const advice = {
6934
+ hint: "once the user confirms the target, run the Next step",
6935
+ next: [rerun({
6936
+ remove: [CONFIRM_FLAG.flag],
6937
+ add: [CONFIRM_FLAG.flag, expected]
6938
+ })]
6991
6939
  };
6992
- const parent = dirname(folder);
6993
- return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
6994
- }
6995
- /** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
6996
- function projectOf(projectFile, cwd, scope) {
6997
- return projectFrom(projectFile, resolve(cwd), scope);
6940
+ if (given === void 0) throw new UsageError({
6941
+ code: CONFIRMATION_REQUIRED,
6942
+ message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
6943
+ ...advice
6944
+ });
6945
+ throw new UsageError({
6946
+ code: CONFIRMATION_MISMATCH,
6947
+ message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
6948
+ ...advice
6949
+ });
6998
6950
  }
6999
6951
  //#endregion
7000
- //#region ../agent-cli/dist/context/secrets.js
7001
- const SECRET_MISSING = "SECRET_MISSING";
7002
- /** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
7003
- function envFileOf(cliName, env) {
7004
- const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
7005
- return join(config, cliName, ".env");
7006
- }
7007
- function readDotenv(path, fileError) {
7008
- let text;
7009
- try {
7010
- text = readFileSync(path, "utf8");
7011
- } catch (error) {
7012
- if (isMissingFile(error)) return {};
7013
- throw fileError(error, {
7014
- path,
7015
- access: "read"
7016
- });
7017
- }
7018
- return parseEnv(text);
7019
- }
7020
- /** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
7021
- function secretOf({ envFile, env, fileError }, name) {
7022
- const fromEnv = env[name];
7023
- if (fromEnv) return {
7024
- value: fromEnv,
7025
- origin: { kind: "env" }
7026
- };
7027
- const fromFile = readDotenv(envFile, fileError)[name];
7028
- return fromFile ? {
7029
- value: fromFile,
7030
- origin: {
7031
- kind: "file",
7032
- path: envFile
7033
- }
7034
- } : void 0;
7035
- }
7036
- function missingIn(scope, secrets) {
7037
- return secrets.filter(({ name }) => secretOf(scope, name) === void 0);
7038
- }
7039
- function getItFrom({ get }) {
7040
- return `(get it from ${get})`;
7041
- }
7042
- function whereToGet(secrets) {
7043
- return secrets.map((secret) => `${secret.name} ${getItFrom(secret)}`).join(", ");
7044
- }
7045
- /** Fails with `SECRET_MISSING`, naming every missing Secret at once, unless all are set. */
7046
- function checkSecrets(secrets, scope, rerun) {
7047
- const missing = missingIn(scope, secrets);
7048
- if (missing.length === 0) return;
7049
- const names = missing.map(({ name }) => name).join(", ");
6952
+ //#region ../agent-cli/dist/context/cost.js
6953
+ const NOT_CONFIRMED = "NOT_CONFIRMED";
6954
+ /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
6955
+ function checkCost({ rerun, commandLine, yes }, cost) {
6956
+ if (cost === void 0 || yes) return;
7050
6957
  throw new CliError({
7051
- code: SECRET_MISSING,
7052
- message: `${missing.length === 1 ? "a Secret it needs is" : "Secrets it needs are"} not set: ${names}`,
7053
- hint: `set ${whereToGet(missing)} in the environment or in ${scope.envFile}, then run the Next step`,
7054
- next: [rerun]
6958
+ code: NOT_CONFIRMED,
6959
+ message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
6960
+ hint: "once the user approves the cost, run the Next step",
6961
+ next: [rerun({ add: [YES_FLAG.flag] })]
7055
6962
  });
7056
6963
  }
7057
- function originOf({ origin }) {
7058
- return origin.kind === "env" ? "the environment" : origin.path;
7059
- }
7060
- /** The `doctor` row over every distinct Secret the CLI and its Commands declare; none without any. */
7061
- function secretRows(secrets, commands) {
7062
- const declared = declaredIn(secrets, commands, (command) => command.secrets);
7063
- if (declared.length === 0) return [];
7064
- return [{
7065
- name: "secrets",
7066
- run: ({ env, envFile, fileError }) => {
7067
- const read = declared.map((declaration) => ({
7068
- declaration,
7069
- secret: secretOf({
7070
- env,
7071
- envFile,
7072
- fileError
7073
- }, declaration.name)
7074
- }));
7075
- const missing = read.filter(({ secret }) => secret === void 0);
7076
- if (missing.length > 0) return {
7077
- state: "problem",
7078
- message: `${missing.map(({ declaration }) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
7079
- };
7080
- return {
7081
- state: "ok",
7082
- message: read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]).join(", ")
7083
- };
7084
- }
7085
- }];
7086
- }
7087
- //#endregion
7088
- //#region ../agent-cli/dist/version.js
7089
- /** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
7090
- function buildStamp() {
7091
- return {
7092
- "sha": "ed7073b",
7093
- "dirty": false,
7094
- "builtAt": "2026-11-06T05:43:19.000Z"
7095
- };
7096
- }
7097
- /** The first line of `--version`: the version, and the commit and time of a stamped build. */
7098
- function versionLine({ version }) {
7099
- const build = buildStamp();
7100
- return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
7101
- }
7102
6964
  //#endregion
7103
6965
  //#region ../agent-cli/dist/context/agent.js
7104
6966
  const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
@@ -7134,49 +6996,788 @@ function isAgent(env) {
7134
6996
  return MARKERS.some((marker) => marker(env));
7135
6997
  }
7136
6998
  //#endregion
7137
- //#region ../agent-cli/dist/built-ins/semver.js
7138
- const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
7139
- const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
7140
- function isVersion(text) {
7141
- return VERSION.test(text);
7142
- }
7143
- function compareIdentifiers(x, y) {
7144
- const xNumeric = /^\d+$/.test(x);
7145
- const yNumeric = /^\d+$/.test(y);
7146
- if (xNumeric && yNumeric) return Number(x) - Number(y);
7147
- if (xNumeric !== yNumeric) return xNumeric ? -1 : 1;
7148
- if (x === y) return 0;
7149
- return x < y ? -1 : 1;
7150
- }
7151
- function comparePrerelease(a, b) {
7152
- if (a === b) return 0;
7153
- if (a === void 0) return 1;
7154
- if (b === void 0) return -1;
7155
- const left = a.split(".");
7156
- const right = b.split(".");
7157
- for (let i = 0; i < Math.min(left.length, right.length); i += 1) {
7158
- const difference = compareIdentifiers(left[i] ?? "", right[i] ?? "");
7159
- if (difference !== 0) return difference;
7160
- }
7161
- return left.length - right.length;
6999
+ //#region ../agent-cli/dist/context/errno.js
7000
+ const PERMISSION_DENIED = "PERMISSION_DENIED";
7001
+ const DISK_FULL = "DISK_FULL";
7002
+ const NOT_FOUND = "NOT_FOUND";
7003
+ const ALREADY_EXISTS = "ALREADY_EXISTS";
7004
+ const NOT_A_FILE = "NOT_A_FILE";
7005
+ function permissionAdvice({ path, access, rerun, env, platform }) {
7006
+ if (platform === "win32") return {
7007
+ hint: `a program may hold ${path} open: close it, then run the Next step`,
7008
+ next: [rerun]
7009
+ };
7010
+ if (!isAgent(env)) return { hint: `check the permissions of ${path} and its folder` };
7011
+ return {
7012
+ hint: `your sandbox may block it: ask your user to type ! then the Next step, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`,
7013
+ next: [rerun]
7014
+ };
7162
7015
  }
7163
- /** Negative when `a` is older than `b`, positive when newer, 0 when the same release. */
7164
- function compareVersions(a, b) {
7165
- const left = VERSION.exec(a);
7166
- const right = VERSION.exec(b);
7167
- if (left === null || right === null) throw new Error(`cannot compare ${a} with ${b}: both must be X.Y.Z versions`);
7168
- for (const part of [
7169
- 1,
7170
- 2,
7171
- 3
7172
- ]) {
7173
- const difference = Number(left[part]) - Number(right[part]);
7174
- if (difference !== 0) return difference;
7175
- }
7176
- return comparePrerelease(left[4], right[4]);
7016
+ function fileErrorOf(scope) {
7017
+ return (error, at) => classifyErrno(error, {
7018
+ ...at,
7019
+ ...scope
7020
+ });
7177
7021
  }
7178
- /** Where `version` stands against `latest`. */
7179
- function standingOf$1(version, latest) {
7022
+ function classifyErrno(error, context) {
7023
+ const { path } = context;
7024
+ switch (errnoOf(error)) {
7025
+ case "EACCES":
7026
+ case "EPERM":
7027
+ case "EROFS":
7028
+ case "EBUSY": return new CliError({
7029
+ code: PERMISSION_DENIED,
7030
+ message: `permission denied: ${path}`,
7031
+ ...permissionAdvice(context)
7032
+ }, { cause: error });
7033
+ case "ENOSPC":
7034
+ case "EDQUOT": return new CliError({
7035
+ code: DISK_FULL,
7036
+ message: `no space left on the disk holding ${path}`,
7037
+ hint: "free space on that disk, then run the Next step",
7038
+ next: [context.rerun]
7039
+ }, { cause: error });
7040
+ case "ENOENT":
7041
+ case "ENOTDIR":
7042
+ case "ENAMETOOLONG": return new CliError({
7043
+ code: NOT_FOUND,
7044
+ message: `not found: ${path}`,
7045
+ hint: `check that ${path} and its folder exist`
7046
+ }, { cause: error });
7047
+ case "EEXIST": return new CliError({
7048
+ code: ALREADY_EXISTS,
7049
+ message: `already exists: ${path}`,
7050
+ hint: `pass another path, or remove ${path}`
7051
+ }, { cause: error });
7052
+ case "EISDIR": return new CliError({
7053
+ code: NOT_A_FILE,
7054
+ message: `not a file: ${path} is a folder`,
7055
+ hint: `pass the path of a file, not of the folder ${path}`
7056
+ }, { cause: error });
7057
+ default: return error;
7058
+ }
7059
+ }
7060
+ //#endregion
7061
+ //#region ../agent-cli/dist/context/interrupts.js
7062
+ const INTERRUPTED = "INTERRUPTED";
7063
+ const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
7064
+ /** How long an interrupted Command may take to settle before the run ends without it. */
7065
+ const GRACE_MS = 3e3;
7066
+ const GRACE_OVER = Symbol("grace over");
7067
+ function interruptedError(rerun) {
7068
+ return new CliError({
7069
+ code: INTERRUPTED,
7070
+ message: "interrupted before it finished",
7071
+ hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
7072
+ next: [rerun]
7073
+ });
7074
+ }
7075
+ const BOUND_WORDS = {
7076
+ deadline: "deadline",
7077
+ ignoreStop: "timeout"
7078
+ };
7079
+ function waitHint(bound) {
7080
+ return bound === "ignoreStop" ? "the call may have been billed: ask your user before running the Next step" : "run the Next step later, when the service it waits on may answer faster";
7081
+ }
7082
+ function deadlineError(commandLine, ms, advice, bound = "deadline") {
7083
+ return new CliError({
7084
+ code: DEADLINE_EXCEEDED,
7085
+ message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
7086
+ ...advice
7087
+ });
7088
+ }
7089
+ /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
7090
+ var RunControl = class {
7091
+ signal;
7092
+ #controller = new AbortController();
7093
+ #commandLine;
7094
+ #rerun;
7095
+ #outer;
7096
+ #onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
7097
+ /** Each running must-finish step, tracked until it settles, with its name. */
7098
+ #steps = /* @__PURE__ */ new Map();
7099
+ #progress;
7100
+ #clock;
7101
+ #disposed = new AbortController();
7102
+ /** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
7103
+ #abortReasons = /* @__PURE__ */ new WeakSet();
7104
+ #graceCancel = new AbortController();
7105
+ #endGrace = () => {};
7106
+ #graceOver = new Promise((resolve) => {
7107
+ this.#endGrace = resolve;
7108
+ });
7109
+ constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
7110
+ this.#commandLine = commandLine;
7111
+ this.#rerun = rerun;
7112
+ this.#outer = outer;
7113
+ this.#clock = clock;
7114
+ this.#progress = progress;
7115
+ this.signal = this.#controller.signal;
7116
+ this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
7117
+ if (outer?.aborted === true) this.#onOuterAbort();
7118
+ else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
7119
+ if (runDeadline !== void 0) {
7120
+ const { ms, advice } = runDeadline;
7121
+ after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
7122
+ }
7123
+ }
7124
+ deadline(ms, options = {}) {
7125
+ return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
7126
+ }
7127
+ #abortsAfter(ms, bound) {
7128
+ const controller = new AbortController();
7129
+ after(this.#clock, ms, this.#disposed.signal, () => {
7130
+ const advice = {
7131
+ hint: waitHint(bound),
7132
+ next: [this.#rerun]
7133
+ };
7134
+ const reason = deadlineError(this.#commandLine, ms, advice, bound);
7135
+ this.#abortReasons.add(reason);
7136
+ controller.abort(reason);
7137
+ });
7138
+ return controller.signal;
7139
+ }
7140
+ async runToEnd(name, work) {
7141
+ this.#graceCancel.abort();
7142
+ if (this.signal.aborted) this.#finishing(name);
7143
+ const step = work();
7144
+ const settled = step.then(() => {}, () => {});
7145
+ this.#steps.set(settled, name);
7146
+ try {
7147
+ return await step;
7148
+ } finally {
7149
+ this.#steps.delete(settled);
7150
+ this.#startGrace();
7151
+ }
7152
+ }
7153
+ /** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
7154
+ async settle(work) {
7155
+ try {
7156
+ const value = await Promise.race([work, this.#graceOver]);
7157
+ if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
7158
+ return value;
7159
+ } finally {
7160
+ await this.#stepsDone();
7161
+ this.close();
7162
+ }
7163
+ }
7164
+ /**
7165
+ * The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
7166
+ * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
7167
+ * Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
7168
+ * reported as that abort, so a declared error keeps its own code.
7169
+ */
7170
+ errorAfter(error) {
7171
+ if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
7172
+ const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
7173
+ return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
7174
+ }
7175
+ #abortIn(error) {
7176
+ if (error instanceof CliError) return void 0;
7177
+ return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
7178
+ }
7179
+ #onStop() {
7180
+ for (const name of this.#steps.values()) this.#finishing(name);
7181
+ this.#startGrace();
7182
+ }
7183
+ #finishing(name) {
7184
+ this.#progress(`finishing ${name}`);
7185
+ }
7186
+ #startGrace() {
7187
+ if (this.signal.aborted && this.#steps.size === 0) {
7188
+ this.#graceCancel.abort();
7189
+ this.#graceCancel = new AbortController();
7190
+ after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
7191
+ }
7192
+ }
7193
+ async #stepsDone() {
7194
+ while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
7195
+ }
7196
+ /** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
7197
+ close() {
7198
+ this.#graceCancel.abort();
7199
+ this.#disposed.abort();
7200
+ this.#outer?.removeEventListener("abort", this.#onOuterAbort);
7201
+ }
7202
+ };
7203
+ //#endregion
7204
+ //#region ../agent-cli/dist/context/operator.js
7205
+ const NEEDS_OPERATOR = "NEEDS_OPERATOR";
7206
+ /** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
7207
+ function checkOperator({ operator, commandLine }, env, rerun) {
7208
+ if (!operator || !isAgent(env)) return;
7209
+ throw new CliError({
7210
+ code: NEEDS_OPERATOR,
7211
+ message: `\`${commandLine}\` must be run by a person at a terminal, not a Coding agent`,
7212
+ hint: "ask the operator to type ! then the Next step",
7213
+ next: [rerun]
7214
+ });
7215
+ }
7216
+ //#endregion
7217
+ //#region ../agent-cli/dist/context/files.js
7218
+ const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
7219
+ /** Where the Runtime writes `path` before moving it into place; a dead writer's is swept. */
7220
+ function tempPathOf(path) {
7221
+ return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
7222
+ }
7223
+ function isAlive(pid) {
7224
+ try {
7225
+ process.kill(pid, 0);
7226
+ return true;
7227
+ } catch (error) {
7228
+ return errnoOf(error) === "EPERM";
7229
+ }
7230
+ }
7231
+ async function sweep(folder) {
7232
+ const dead = (await readdir(folder)).filter((name) => {
7233
+ const pid = TEMP_FILE.exec(name)?.[1];
7234
+ return pid !== void 0 && !isAlive(Number(pid));
7235
+ });
7236
+ await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
7237
+ }
7238
+ /** The file a symlink at `path` points to, so a replacing write keeps the link. */
7239
+ async function targetOf$1(path) {
7240
+ try {
7241
+ return await realpath(path);
7242
+ } catch (error) {
7243
+ if (isMissingFile(error)) return path;
7244
+ throw error;
7245
+ }
7246
+ }
7247
+ async function writeWithMode(path, data, mode) {
7248
+ if (mode === void 0) {
7249
+ await writeFile(path, data);
7250
+ return;
7251
+ }
7252
+ await writeFile(path, data, { mode });
7253
+ await chmod(path, mode);
7254
+ }
7255
+ /** What `link` answers on a file system without hard links, such as exFAT. */
7256
+ const NO_HARD_LINKS = /* @__PURE__ */ new Set([
7257
+ "EPERM",
7258
+ "ENOTSUP",
7259
+ "EXDEV"
7260
+ ]);
7261
+ /** A link fails atomically when `path` is taken, so no reader ever sees half a file there. */
7262
+ async function linkNew(temp, path, data, mode) {
7263
+ try {
7264
+ await link(temp, path);
7265
+ } catch (error) {
7266
+ if (!NO_HARD_LINKS.has(errnoOf(error))) throw error;
7267
+ await createNew(path, data, mode);
7268
+ }
7269
+ }
7270
+ /** A file this call created and could not finish is removed, so a rerun does not find half of it. */
7271
+ async function createNew(path, data, mode) {
7272
+ const handle = await open(path, "wx", mode);
7273
+ try {
7274
+ await handle.writeFile(data);
7275
+ if (mode !== void 0) await handle.chmod(mode);
7276
+ } catch (error) {
7277
+ await handle.close();
7278
+ await rm(path, { force: true });
7279
+ throw error;
7280
+ }
7281
+ await handle.close();
7282
+ }
7283
+ const RENAME_ATTEMPTS = 10;
7284
+ const RENAME_BACKOFF_MS = 10;
7285
+ const HELD_BY_ANOTHER_PROGRAM = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
7286
+ async function renameRetrying(from, to) {
7287
+ for (let attempt = 1;; attempt++) try {
7288
+ await rename(from, to);
7289
+ return;
7290
+ } catch (error) {
7291
+ if (!(process.platform === "win32" && HELD_BY_ANOTHER_PROGRAM.has(errnoOf(error))) || attempt === RENAME_ATTEMPTS) throw error;
7292
+ await setTimeout$1(attempt * RENAME_BACKOFF_MS);
7293
+ }
7294
+ }
7295
+ /** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
7296
+ function realFiles() {
7297
+ const swept = /* @__PURE__ */ new Set();
7298
+ return {
7299
+ exists: async (path) => {
7300
+ try {
7301
+ await access(path);
7302
+ return true;
7303
+ } catch (error) {
7304
+ if (isNothingThere(error)) return false;
7305
+ throw error;
7306
+ }
7307
+ },
7308
+ write: async (path, data, options = {}) => {
7309
+ await mkdir(dirname(path), { recursive: true });
7310
+ const target = options.exclusive === true ? path : await targetOf$1(path);
7311
+ const folder = dirname(target);
7312
+ if (!swept.has(folder)) {
7313
+ await sweep(folder);
7314
+ swept.add(folder);
7315
+ }
7316
+ const temp = tempPathOf(target);
7317
+ try {
7318
+ await writeWithMode(temp, data, options.mode);
7319
+ if (options.exclusive === true) {
7320
+ await linkNew(temp, target, data, options.mode);
7321
+ await rm(temp);
7322
+ } else await renameRetrying(temp, target);
7323
+ } catch (error) {
7324
+ await rm(temp, { force: true });
7325
+ throw error;
7326
+ }
7327
+ },
7328
+ read: (path) => readFile(path),
7329
+ list: (folder) => readdir(folder),
7330
+ remove: (path) => rm(path, {
7331
+ recursive: true,
7332
+ force: true
7333
+ }),
7334
+ mkdir: async (path, options) => {
7335
+ const mode = options?.mode;
7336
+ if (options?.exclusive !== true) {
7337
+ await mkdir(path, {
7338
+ recursive: true,
7339
+ mode
7340
+ });
7341
+ return;
7342
+ }
7343
+ await mkdir(dirname(path), {
7344
+ recursive: true,
7345
+ mode
7346
+ });
7347
+ await mkdir(path, { mode });
7348
+ },
7349
+ stat: async (path) => {
7350
+ try {
7351
+ const found = await stat(path, { bigint: true });
7352
+ return {
7353
+ size: Number(found.size),
7354
+ id: `${found.dev}:${found.ino}`,
7355
+ folder: found.isDirectory()
7356
+ };
7357
+ } catch (error) {
7358
+ if (isNothingThere(error)) return void 0;
7359
+ throw error;
7360
+ }
7361
+ },
7362
+ realPath: async (path) => {
7363
+ try {
7364
+ return await realpath(path);
7365
+ } catch (error) {
7366
+ if (isNothingThere(error)) return void 0;
7367
+ throw error;
7368
+ }
7369
+ }
7370
+ };
7371
+ }
7372
+ /**
7373
+ * `files` resolving paths against `cwd`, its failures classified by errno; `list` drops the
7374
+ * Runtime's temp files and sorts.
7375
+ */
7376
+ function classifiedFiles(files, { cwd, fileError }) {
7377
+ async function at(access, path, work) {
7378
+ const target = resolve(cwd, path);
7379
+ try {
7380
+ return await work(target);
7381
+ } catch (error) {
7382
+ throw fileError(error, {
7383
+ path: target,
7384
+ access
7385
+ });
7386
+ }
7387
+ }
7388
+ return {
7389
+ exists: (path) => at("read", path, (target) => files.exists(target)),
7390
+ write: (path, data, options) => at("write", path, (target) => files.write(target, data, options)),
7391
+ read: (path) => at("read", path, (target) => files.read(target)),
7392
+ list: (folder) => at("read", folder, async (target) => (await files.list(target)).filter((name) => !TEMP_FILE.test(name)).sort()),
7393
+ remove: (path) => at("write", path, (target) => files.remove(target)),
7394
+ mkdir: (path, options) => at("write", path, (target) => files.mkdir(target, options)),
7395
+ stat: (path) => at("read", path, (target) => files.stat(target)),
7396
+ realPath: (path) => at("read", path, (target) => files.realPath(target))
7397
+ };
7398
+ }
7399
+ //#endregion
7400
+ //#region ../agent-cli/dist/context/outputs.js
7401
+ const OUTPUT_EXISTS = "OUTPUT_EXISTS";
7402
+ /** The outputs of a run on `files`, whose paths resolve against `cwd`. */
7403
+ function outputsOf(files, { cwd, fileError }) {
7404
+ const classified = classifiedFiles(files, {
7405
+ cwd,
7406
+ fileError
7407
+ });
7408
+ const unwritten = /* @__PURE__ */ new Set();
7409
+ let forceStep = [];
7410
+ const exclusiveWrite = async (path, data, options) => {
7411
+ const target = resolve(cwd, path);
7412
+ unwritten.delete(target);
7413
+ try {
7414
+ await classified.write(path, data, {
7415
+ ...options,
7416
+ exclusive: true
7417
+ });
7418
+ } catch (error) {
7419
+ unwritten.add(target);
7420
+ if (!(error instanceof CliError) || error.code !== "ALREADY_EXISTS") throw error;
7421
+ throw new CliError({
7422
+ code: ALREADY_EXISTS,
7423
+ message: error.message,
7424
+ hint: "once the user agrees to replace it, run the Next step",
7425
+ next: [forceStep]
7426
+ }, { cause: error });
7427
+ }
7428
+ };
7429
+ const guarded = {
7430
+ ...classified,
7431
+ write: (path, data, options) => unwritten.has(resolve(cwd, path)) ? exclusiveWrite(path, data, options) : classified.write(path, data, options)
7432
+ };
7433
+ return {
7434
+ files: guarded,
7435
+ refuseOverwrite: async (outputs, { rerun, commandLine, force }) => {
7436
+ if (force) return;
7437
+ const found = await Promise.all(outputs.map((path) => guarded.exists(path)));
7438
+ const existing = outputs.filter((_path, index) => found[index]);
7439
+ if (existing.length === 0) return;
7440
+ const them = existing.length === 1 ? "it" : "them";
7441
+ throw new CliError({
7442
+ code: OUTPUT_EXISTS,
7443
+ message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
7444
+ hint: `once the user agrees to replace ${them}, run the Next step`,
7445
+ next: [rerun({ add: [FORCE_FLAG.flag] })]
7446
+ });
7447
+ },
7448
+ claim: async (outputs, { rerun, force }) => {
7449
+ await probe(outputs, files, {
7450
+ cwd,
7451
+ fileError
7452
+ });
7453
+ if (force) return;
7454
+ forceStep = rerun({ add: [FORCE_FLAG.flag] });
7455
+ for (const path of outputs) unwritten.add(resolve(cwd, path));
7456
+ }
7457
+ };
7458
+ }
7459
+ async function probe(outputs, files, { cwd, fileError }) {
7460
+ const folders = new Set(outputs.map((path) => dirname(resolve(cwd, path))));
7461
+ await Promise.all([...folders].map(async (folder) => {
7462
+ const probePath = tempPathOf(join(folder, "outputs-probe"));
7463
+ try {
7464
+ await files.mkdir(folder);
7465
+ await files.write(probePath, "");
7466
+ await files.remove(probePath);
7467
+ } catch (error) {
7468
+ throw fileError(error, {
7469
+ path: folder,
7470
+ access: "write"
7471
+ });
7472
+ }
7473
+ }));
7474
+ }
7475
+ //#endregion
7476
+ //#region ../agent-cli/dist/context/project.js
7477
+ const PROJECT_INVALID = "PROJECT_INVALID";
7478
+ function invalid(path, problem, rerun, options) {
7479
+ return new CliError({
7480
+ code: PROJECT_INVALID,
7481
+ message: `the Project file ${path} is invalid: ${problem}`,
7482
+ hint: `fix ${path}, then run the Next step`,
7483
+ next: [rerun]
7484
+ }, options);
7485
+ }
7486
+ function readText(path, fileError) {
7487
+ try {
7488
+ return readFileSync(path, "utf8");
7489
+ } catch (error) {
7490
+ if (isMissingFile(error)) return void 0;
7491
+ throw fileError(error, {
7492
+ path,
7493
+ access: "read"
7494
+ });
7495
+ }
7496
+ }
7497
+ function parsed(path, text, schema, rerun) {
7498
+ let json;
7499
+ try {
7500
+ json = JSON.parse(text);
7501
+ } catch (error) {
7502
+ throw invalid(path, messageOf(error), rerun, { cause: error });
7503
+ }
7504
+ const result = schema.safeParse(json);
7505
+ if (!result.success) throw invalid(path, prettifyError(result.error), rerun);
7506
+ return result.data;
7507
+ }
7508
+ function projectFrom(projectFile, folder, scope) {
7509
+ const path = join(folder, projectFile.file);
7510
+ const text = readText(path, scope.fileError);
7511
+ if (text !== void 0) return {
7512
+ root: folder,
7513
+ settings: parsed(path, text, projectFile.schema, scope.rerun)
7514
+ };
7515
+ const parent = dirname(folder);
7516
+ return parent === folder ? void 0 : projectFrom(projectFile, parent, scope);
7517
+ }
7518
+ /** The nearest Project file at or above `cwd`, parsed by its schema; undefined when there is none. */
7519
+ function projectOf(projectFile, cwd, scope) {
7520
+ return projectFrom(projectFile, resolve(cwd), scope);
7521
+ }
7522
+ //#endregion
7523
+ //#region ../agent-cli/dist/context/secrets.js
7524
+ const SECRET_MISSING = "SECRET_MISSING";
7525
+ /** `$XDG_CONFIG_HOME/<cli>/.env`, with `~/.config` when the variable is unset or empty. */
7526
+ function envFileOf(cliName, env) {
7527
+ const config = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), ".config");
7528
+ return join(config, cliName, ".env");
7529
+ }
7530
+ function readDotenv(path, fileError) {
7531
+ let text;
7532
+ try {
7533
+ text = readFileSync(path, "utf8");
7534
+ } catch (error) {
7535
+ if (isMissingFile(error)) return {};
7536
+ throw fileError(error, {
7537
+ path,
7538
+ access: "read"
7539
+ });
7540
+ }
7541
+ return parseEnv(text);
7542
+ }
7543
+ /** Looks `name` up in the env, then in `envFile`; an empty value counts as unset. */
7544
+ function secretOf({ envFile, env, fileError }, name) {
7545
+ const fromEnv = env[name];
7546
+ if (fromEnv) return {
7547
+ value: fromEnv,
7548
+ origin: { kind: "env" }
7549
+ };
7550
+ const fromFile = readDotenv(envFile, fileError)[name];
7551
+ return fromFile ? {
7552
+ value: fromFile,
7553
+ origin: {
7554
+ kind: "file",
7555
+ path: envFile
7556
+ }
7557
+ } : void 0;
7558
+ }
7559
+ function isNeeded({ optional }) {
7560
+ return optional !== true;
7561
+ }
7562
+ function missingIn(scope, secrets) {
7563
+ return secrets.filter((secret) => isNeeded(secret) && secretOf(scope, secret.name) === void 0);
7564
+ }
7565
+ function getItFrom({ get }) {
7566
+ return `(get it from ${get})`;
7567
+ }
7568
+ function whereToGet(secrets) {
7569
+ return secrets.map((secret) => `${secret.name} ${getItFrom(secret)}`).join(", ");
7570
+ }
7571
+ /** Fails with `SECRET_MISSING`, naming every missing Secret at once, unless all are set. */
7572
+ function checkSecrets(secrets, scope, rerun) {
7573
+ const missing = missingIn(scope, secrets);
7574
+ if (missing.length === 0) return;
7575
+ const names = missing.map(({ name }) => name).join(", ");
7576
+ throw new CliError({
7577
+ code: SECRET_MISSING,
7578
+ message: `${missing.length === 1 ? "a Secret it needs is" : "Secrets it needs are"} not set: ${names}`,
7579
+ hint: `set ${whereToGet(missing)} in the environment or in ${scope.envFile}, then run the Next step`,
7580
+ next: [rerun]
7581
+ });
7582
+ }
7583
+ function originOf({ origin }) {
7584
+ return origin.kind === "env" ? "the environment" : origin.path;
7585
+ }
7586
+ /**
7587
+ * The `doctor` row over every distinct Secret the CLI and its Commands declare; none without any. A
7588
+ * name needed anywhere counts as needed, and an unset optional one goes unmentioned.
7589
+ */
7590
+ function secretRows(secrets, commands) {
7591
+ const declared = declaredIn(secrets, commands, (command) => command.secrets);
7592
+ if (declared.length === 0) return [];
7593
+ const needed = new Map(declaredIn(secrets.filter(isNeeded), commands, (command) => command.secrets?.filter(isNeeded)).map((declaration) => [declaration.name, declaration]));
7594
+ return [{
7595
+ name: "secrets",
7596
+ run: ({ env, envFile, fileError }) => {
7597
+ const read = declared.map((declaration) => ({
7598
+ declaration,
7599
+ secret: secretOf({
7600
+ env,
7601
+ envFile,
7602
+ fileError
7603
+ }, declaration.name)
7604
+ }));
7605
+ const missing = read.flatMap(({ declaration, secret }) => {
7606
+ const neededAs = needed.get(declaration.name);
7607
+ return secret === void 0 && neededAs !== void 0 ? [neededAs] : [];
7608
+ });
7609
+ if (missing.length > 0) return {
7610
+ state: "problem",
7611
+ message: `${missing.map((declaration) => `${declaration.name} is not set ${getItFrom(declaration)}`).join(", ")}; add ${missing.length === 1 ? "it" : "them"} to ${envFile} or the environment`
7612
+ };
7613
+ const found = read.flatMap(({ declaration, secret }) => secret === void 0 ? [] : [`${declaration.name} from ${originOf(secret)}`]);
7614
+ return {
7615
+ state: "ok",
7616
+ message: found.length > 0 ? found.join(", ") : "no Secret is set, and none is needed"
7617
+ };
7618
+ }
7619
+ }];
7620
+ }
7621
+ //#endregion
7622
+ //#region ../agent-cli/dist/runtime-codes.js
7623
+ const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
7624
+ const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
7625
+ const NOT_ON_PATH = "NOT_ON_PATH";
7626
+ const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
7627
+ const REGISTRY_AUTH = "REGISTRY_AUTH";
7628
+ const INSTALL_FAILED = "INSTALL_FAILED";
7629
+ const NPM_FAILED = "NPM_FAILED";
7630
+ const NO_LATEST = "NO_LATEST";
7631
+ /** The Runtime codes any Command can meet, which no Command declares. */
7632
+ const anyCommandErrors = {
7633
+ [INVALID_INPUT]: { class: "caller" },
7634
+ [UNKNOWN_OPTION]: { class: "caller" },
7635
+ [UNKNOWN_COMMAND]: { class: "caller" },
7636
+ [UNKNOWN_FIELD]: { class: "caller" },
7637
+ [CONFIRMATION_REQUIRED]: { class: "caller" },
7638
+ [CONFIRMATION_MISMATCH]: { class: "caller" },
7639
+ [NOT_CONFIRMED]: { class: "operator" },
7640
+ [NEEDS_OPERATOR]: { class: "operator" },
7641
+ [OUTPUT_EXISTS]: { class: "caller" },
7642
+ [STDIN_STALLED]: { class: "caller" },
7643
+ [INTERRUPTED]: { class: "interrupted" },
7644
+ [DEADLINE_EXCEEDED]: { class: "wait" },
7645
+ [INTERNAL]: { class: "bug" },
7646
+ [PERMISSION_DENIED]: { class: "operator" },
7647
+ [DISK_FULL]: { class: "operator" },
7648
+ [NOT_FOUND]: { class: "caller" },
7649
+ [ALREADY_EXISTS]: { class: "caller" },
7650
+ [NOT_A_FILE]: { class: "caller" },
7651
+ [TOOL_FAILED]: { class: "operator" },
7652
+ [TOOL_MISSING]: { class: "operator" },
7653
+ [SECRET_MISSING]: { class: "operator" },
7654
+ [PROJECT_INVALID]: { class: "operator" }
7655
+ };
7656
+ /**
7657
+ * The Runtime codes only some Commands reach: each Command that throws one, Built-in or the CLI's
7658
+ * own, declares it in `errors`, as it does a code of the CLI's Error table.
7659
+ */
7660
+ const declaredRuntimeErrors = {
7661
+ [DOCTOR_PROBLEMS]: { class: "operator" },
7662
+ [NOT_GLOBAL_INSTALL]: { class: "operator" },
7663
+ [NOT_ON_PATH]: { class: "operator" },
7664
+ [REGISTRY_UNREACHABLE]: { class: "operator" },
7665
+ [REGISTRY_AUTH]: { class: "operator" },
7666
+ [INSTALL_FAILED]: { class: "operator" },
7667
+ [NPM_FAILED]: { class: "operator" },
7668
+ [NO_LATEST]: { class: "source" }
7669
+ };
7670
+ /** The Runtime's own Error table: every code it fails a run with, and the class that must act. */
7671
+ const runtimeErrors = {
7672
+ ...anyCommandErrors,
7673
+ ...declaredRuntimeErrors
7674
+ };
7675
+ const anyCommandClasses = Object.values(anyCommandErrors).map((declaration) => declaration.class);
7676
+ function isRuntimeCode(code) {
7677
+ return Object.hasOwn(runtimeErrors, code);
7678
+ }
7679
+ function isDeclaredRuntimeCode(code) {
7680
+ return Object.hasOwn(declaredRuntimeErrors, code);
7681
+ }
7682
+ /** Whether a Command or `doctor` row throwing `code` must declare it: a code of `table`, or a Runtime one only some reach. */
7683
+ function mustDeclare(code, table) {
7684
+ return Object.hasOwn(table, code) || isDeclaredRuntimeCode(code);
7685
+ }
7686
+ //#endregion
7687
+ //#region ../agent-cli/dist/define-command.js
7688
+ /** Throws when the Command's declaration breaks a rule its type cannot express; `label` names it. */
7689
+ function checkCommand(command, label, table) {
7690
+ checkNoStandardFlags(command, label);
7691
+ checkForce(command, label);
7692
+ checkArrayArgLast(label, command.args);
7693
+ checkStdinFields(label, command.args, command.options);
7694
+ checkNoBooleanArrayOption(label, command.options);
7695
+ checkErrors(command.errors ?? [], label, table);
7696
+ }
7697
+ /** Throws on a code in `errors` that is neither in `table` nor a Runtime code some Commands declare. */
7698
+ function checkErrors(errors, label, table) {
7699
+ const unknown = errors.filter((code) => !mustDeclare(code, table));
7700
+ if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
7701
+ }
7702
+ function defineCommandFor() {
7703
+ return declared;
7704
+ }
7705
+ function declared(command) {
7706
+ return command;
7707
+ }
7708
+ const defineCommand$1 = defineCommandFor();
7709
+ //#endregion
7710
+ //#region ../agent-cli/dist/error-table.js
7711
+ /** The Error table a CLI declares, empty when it declares none. */
7712
+ function errorTableOf({ errors }) {
7713
+ return errors ?? {};
7714
+ }
7715
+ /** The `CliError` for a code of `Table`, as `cliError` and `ctx.fail` build it. */
7716
+ function cliErrorOf(code, init, options) {
7717
+ return new CliError({
7718
+ ...init,
7719
+ code
7720
+ }, options);
7721
+ }
7722
+ //#endregion
7723
+ //#region ../agent-cli/dist/version.js
7724
+ /** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
7725
+ function buildStamp() {
7726
+ return {
7727
+ "sha": "b509cf2",
7728
+ "dirty": false,
7729
+ "builtAt": "2026-11-09T01:38:28.000Z"
7730
+ };
7731
+ }
7732
+ /** The first line of `--version`: the version, and the commit and time of a stamped build. */
7733
+ function versionLine({ version }) {
7734
+ const build = buildStamp();
7735
+ return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
7736
+ }
7737
+ //#endregion
7738
+ //#region ../agent-cli/dist/built-ins/semver.js
7739
+ const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
7740
+ const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
7741
+ function isVersion(text) {
7742
+ return VERSION.test(text);
7743
+ }
7744
+ function compareIdentifiers(x, y) {
7745
+ const xNumeric = /^\d+$/.test(x);
7746
+ const yNumeric = /^\d+$/.test(y);
7747
+ if (xNumeric && yNumeric) return Number(x) - Number(y);
7748
+ if (xNumeric !== yNumeric) return xNumeric ? -1 : 1;
7749
+ if (x === y) return 0;
7750
+ return x < y ? -1 : 1;
7751
+ }
7752
+ function comparePrerelease(a, b) {
7753
+ if (a === b) return 0;
7754
+ if (a === void 0) return 1;
7755
+ if (b === void 0) return -1;
7756
+ const left = a.split(".");
7757
+ const right = b.split(".");
7758
+ for (let i = 0; i < Math.min(left.length, right.length); i += 1) {
7759
+ const difference = compareIdentifiers(left[i] ?? "", right[i] ?? "");
7760
+ if (difference !== 0) return difference;
7761
+ }
7762
+ return left.length - right.length;
7763
+ }
7764
+ /** Negative when `a` is older than `b`, positive when newer, 0 when the same release. */
7765
+ function compareVersions(a, b) {
7766
+ const left = VERSION.exec(a);
7767
+ const right = VERSION.exec(b);
7768
+ if (left === null || right === null) throw new Error(`cannot compare ${a} with ${b}: both must be X.Y.Z versions`);
7769
+ for (const part of [
7770
+ 1,
7771
+ 2,
7772
+ 3
7773
+ ]) {
7774
+ const difference = Number(left[part]) - Number(right[part]);
7775
+ if (difference !== 0) return difference;
7776
+ }
7777
+ return comparePrerelease(left[4], right[4]);
7778
+ }
7779
+ /** Where `version` stands against `latest`. */
7780
+ function standingOf$1(version, latest) {
7180
7781
  const difference = compareVersions(version, latest);
7181
7782
  if (difference < 0) return "behind";
7182
7783
  return difference > 0 ? "ahead" : "current";
@@ -7196,12 +7797,15 @@ function meetsMinimum(version, minimum) {
7196
7797
  }
7197
7798
  //#endregion
7198
7799
  //#region ../agent-cli/dist/built-ins/npm.js
7199
- const REGISTRY_UNREACHABLE = "REGISTRY_UNREACHABLE";
7200
- const REGISTRY_AUTH = "REGISTRY_AUTH";
7201
- const INSTALL_FAILED = "INSTALL_FAILED";
7202
- const NPM_FAILED = "NPM_FAILED";
7203
- const NO_LATEST = "NO_LATEST";
7204
- function npmContext(cli, registry, ctx, node, cwd) {
7800
+ /** What a Command running npm may fail with, for it to declare in `errors`. */
7801
+ const NPM_ERRORS = [
7802
+ REGISTRY_UNREACHABLE,
7803
+ REGISTRY_AUTH,
7804
+ INSTALL_FAILED,
7805
+ NPM_FAILED,
7806
+ NO_LATEST
7807
+ ];
7808
+ function npmContext(cli, registry, ctx, { node, platform }, cwd) {
7205
7809
  return {
7206
7810
  cliName: cli.name,
7207
7811
  rerun: ctx.rerun(),
@@ -7209,7 +7813,9 @@ function npmContext(cli, registry, ctx, node, cwd) {
7209
7813
  cwd,
7210
7814
  env: ctx.env,
7211
7815
  signal: ctx.signal,
7212
- node
7816
+ node,
7817
+ platform,
7818
+ files: ctx.files
7213
7819
  };
7214
7820
  }
7215
7821
  const STDERR_TAIL_LINES = 10;
@@ -7251,9 +7857,10 @@ function setupCommandLine({ package: name, flags }) {
7251
7857
  "setup"
7252
7858
  ].join(" ");
7253
7859
  }
7254
- function npmOf(ctx, windows) {
7860
+ async function npmOf(ctx) {
7861
+ const windows = ctx.platform === "win32";
7255
7862
  const beside = ctx.node === void 0 ? void 0 : join(dirname(ctx.node), windows ? "npm.cmd" : "npm");
7256
- if (beside === void 0 || !existsSync(beside)) return {
7863
+ if (beside === void 0 || !await ctx.files.exists(beside)) return {
7257
7864
  path: "npm",
7258
7865
  env: ctx.env
7259
7866
  };
@@ -7270,8 +7877,7 @@ function npmOf(ctx, windows) {
7270
7877
  }
7271
7878
  };
7272
7879
  }
7273
- function spawnNpm(args, ctx, signal) {
7274
- const npm = npmOf(ctx, process.platform === "win32");
7880
+ function spawnNpm(npm, args, ctx, signal) {
7275
7881
  return spawnChild(npm.path, args, {
7276
7882
  cwd: ctx.cwd,
7277
7883
  env: npm.env,
@@ -7289,7 +7895,7 @@ function authHint(code, ctx) {
7289
7895
  if (code !== "E403" || !isAgent(ctx.env)) return login;
7290
7896
  return `a Coding agent's sandbox may refuse ${registry} with a 403: ask your user to type ! and ${RUN_AGAIN}, or to allow ${registry} in the sandbox's network list; if it fails outside the sandbox too, ${login}`;
7291
7897
  }
7292
- function failureOf(call, answer, ctx) {
7898
+ function failureOf$1(call, answer, ctx) {
7293
7899
  const { label: registry, package: name, unreachableHint } = ctx.registry;
7294
7900
  const code = NPM_CODE.exec(answer.stderr)?.[1];
7295
7901
  const output = tail$1(answer.stderr);
@@ -7334,7 +7940,7 @@ function noLatest(ctx, message) {
7334
7940
  async function runNpm(call, args, ctx, signal = ctx.signal) {
7335
7941
  const cache = await mkdtemp(join(tmpdir(), `${ctx.cliName}-npm-`));
7336
7942
  try {
7337
- const answer = await spawnNpm([
7943
+ const answer = await spawnNpm(await npmOf(ctx), [
7338
7944
  ...args,
7339
7945
  `--cache=${join(cache, "cache")}`,
7340
7946
  "--logs-max=0"
@@ -7346,7 +7952,7 @@ async function runNpm(call, args, ctx, signal = ctx.signal) {
7346
7952
  hint: "install Node with npm, or put npm on PATH"
7347
7953
  }, { cause: error });
7348
7954
  });
7349
- if (answer.exitCode !== 0) throw failureOf(call, answer, ctx);
7955
+ if (answer.exitCode !== 0) throw failureOf$1(call, answer, ctx);
7350
7956
  return answer.stdout;
7351
7957
  } finally {
7352
7958
  await rm(cache, {
@@ -7396,6 +8002,30 @@ async function latestVersion(ctx) {
7396
8002
  return version;
7397
8003
  }
7398
8004
  //#endregion
8005
+ //#region ../agent-cli/dist/built-ins/read.js
8006
+ function isNotFound(error) {
8007
+ return error instanceof CliError && error.code === "NOT_FOUND";
8008
+ }
8009
+ const decoder = new TextDecoder();
8010
+ /** The text of the file at `path`, or `undefined` when nothing is there. */
8011
+ async function readIfAny(files, path) {
8012
+ try {
8013
+ return decoder.decode(await files.read(path));
8014
+ } catch (error) {
8015
+ if (isNotFound(error)) return void 0;
8016
+ throw error;
8017
+ }
8018
+ }
8019
+ /** The text of a file the CLI's own package ships, whose absence is the release's bug. */
8020
+ async function readRequired(files, path) {
8021
+ try {
8022
+ return decoder.decode(await files.read(path));
8023
+ } catch (error) {
8024
+ if (!isNotFound(error)) throw error;
8025
+ throw new Error(`the package is missing its own file ${path}`, { cause: error });
8026
+ }
8027
+ }
8028
+ //#endregion
7399
8029
  //#region ../agent-cli/dist/built-ins/project-root.js
7400
8030
  const MANAGERS = [
7401
8031
  "pnpm",
@@ -7420,22 +8050,14 @@ function ancestorsOf(dir) {
7420
8050
  const parent = dirname(dir);
7421
8051
  return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
7422
8052
  }
7423
- async function exists(path) {
7424
- return await orMissing(stat(path)) !== void 0;
7425
- }
7426
8053
  /** Whether `path` lies strictly inside `dir`. */
7427
8054
  function isInside(dir, path) {
7428
8055
  const inner = relative(dir, path);
7429
8056
  return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
7430
8057
  }
7431
- async function manifestIn(dir) {
7432
- let text;
7433
- try {
7434
- text = await readFile(join(dir, "package.json"), "utf8");
7435
- } catch (error) {
7436
- if (isMissingFile(error)) return void 0;
7437
- throw error;
7438
- }
8058
+ async function manifestIn(files, dir) {
8059
+ const text = await readIfAny(files, join(dir, "package.json"));
8060
+ if (text === void 0) return void 0;
7439
8061
  let manifest;
7440
8062
  try {
7441
8063
  manifest = JSON.parse(text);
@@ -7444,19 +8066,19 @@ async function manifestIn(dir) {
7444
8066
  }
7445
8067
  return manifestSchema$1.safeParse(manifest).data;
7446
8068
  }
7447
- async function declaredManager(dir) {
7448
- const name = (await manifestIn(dir))?.packageManager?.split("@")[0];
8069
+ async function declaredManager(files, dir) {
8070
+ const name = (await manifestIn(files, dir))?.packageManager?.split("@")[0];
7449
8071
  return MANAGERS.find((manager) => manager === name);
7450
8072
  }
7451
- async function lockfileRootOf(cwd) {
7452
- for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await exists(join(dir, file))) return {
8073
+ async function lockfileRootOf(files, cwd) {
8074
+ for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await files.exists(join(dir, file))) return {
7453
8075
  dir,
7454
- manager: await declaredManager(dir) ?? kind
8076
+ manager: await declaredManager(files, dir) ?? kind
7455
8077
  };
7456
8078
  }
7457
8079
  /** The package manager's project root: the folder of the nearest lockfile, or `cwd` without one. */
7458
- async function projectRootOf(cwd) {
7459
- return (await lockfileRootOf(cwd))?.dir ?? cwd;
8080
+ async function projectRootOf(files, cwd) {
8081
+ return (await lockfileRootOf(files, cwd))?.dir ?? cwd;
7460
8082
  }
7461
8083
  //#endregion
7462
8084
  //#region ../agent-cli/dist/built-ins/install/run-as.js
@@ -7479,29 +8101,30 @@ const LINES = {
7479
8101
  }
7480
8102
  };
7481
8103
  const packageSchema = object({ version: string() });
7482
- async function manifestListing(name, cwd, root) {
8104
+ async function manifestListing(files, name, cwd, root) {
7483
8105
  const dirs = ancestorsOf(cwd);
7484
8106
  const top = root === void 0 ? dirs.length : dirs.indexOf(root.dir) + 1;
7485
8107
  for (const dir of dirs.slice(0, top)) {
7486
- const manifest = await manifestIn(dir);
8108
+ const manifest = await manifestIn(files, dir);
7487
8109
  if (manifest?.dependencies?.[name] !== void 0 || manifest?.devDependencies?.[name] !== void 0) return join(dir, "package.json");
7488
8110
  }
7489
8111
  }
7490
- async function versionIn(manifest) {
8112
+ function versionIn(text) {
7491
8113
  try {
7492
- return packageSchema.parse(JSON.parse(await readFile(manifest, "utf8"))).version;
8114
+ return packageSchema.parse(JSON.parse(text)).version;
7493
8115
  } catch (error) {
7494
8116
  if (error instanceof SyntaxError || error instanceof ZodError) return { reason: messageOf(error) };
7495
8117
  throw error;
7496
8118
  }
7497
8119
  }
7498
- async function standingOf(install, bin, cwd, root) {
8120
+ async function standingOf(files, install, bin, cwd, root) {
7499
8121
  if (bin === void 0) return { kind: "source" };
7500
8122
  for (const projectDir of ancestorsOf(cwd)) {
7501
8123
  const folder = join(projectDir, "node_modules", install.package);
7502
8124
  const manifest = join(folder, "package.json");
7503
- if (!await exists(manifest)) continue;
7504
- const version = await versionIn(manifest);
8125
+ const text = await readIfAny(files, manifest);
8126
+ if (text === void 0) continue;
8127
+ const version = versionIn(text);
7505
8128
  if (typeof version !== "string") return {
7506
8129
  kind: "unreadable",
7507
8130
  manifest,
@@ -7509,14 +8132,14 @@ async function standingOf(install, bin, cwd, root) {
7509
8132
  };
7510
8133
  const copy = {
7511
8134
  projectDir,
7512
- realDir: await realpath(folder),
8135
+ realDir: await files.realPath(folder) ?? folder,
7513
8136
  version
7514
8137
  };
7515
8138
  if (!copy.realDir.split(sep).includes("node_modules")) return {
7516
8139
  kind: "linked",
7517
8140
  copy
7518
8141
  };
7519
- const runningFrom = await orMissing(realpath(bin)) ?? bin;
8142
+ const runningFrom = await files.realPath(bin) ?? bin;
7520
8143
  return isInside(copy.realDir, runningFrom) ? {
7521
8144
  kind: "installed",
7522
8145
  copy
@@ -7526,18 +8149,18 @@ async function standingOf(install, bin, cwd, root) {
7526
8149
  runningFrom
7527
8150
  };
7528
8151
  }
7529
- const manifest = await manifestListing(install.package, cwd, root);
8152
+ const manifest = await manifestListing(files, install.package, cwd, root);
7530
8153
  return manifest === void 0 ? { kind: "missing" } : {
7531
8154
  kind: "listed",
7532
8155
  manifest
7533
8156
  };
7534
8157
  }
7535
- async function surveyDependency(install, bin, cwd) {
7536
- const root = await lockfileRootOf(cwd);
8158
+ async function surveyDependency(install, bin, { files, cwd }) {
8159
+ const root = await lockfileRootOf(files, cwd);
7537
8160
  return {
7538
8161
  cwd,
7539
8162
  root,
7540
- standing: await standingOf(install, bin, cwd, root)
8163
+ standing: await standingOf(files, install, bin, cwd, root)
7541
8164
  };
7542
8165
  }
7543
8166
  function packagesOf(install) {
@@ -7584,269 +8207,69 @@ function installFinding(cli, runAs, install, { cwd, root, standing }) {
7584
8207
  const where = root === void 0 ? copy.projectDir : `${root.dir} with ${root.manager}`;
7585
8208
  return {
7586
8209
  state: "ok",
7587
- message: `${install.package} ${copy.version}, installed in ${where}`
7588
- };
7589
- }
7590
- }
7591
- }
7592
- function registryOf({ package: name }, root) {
7593
- const npmrc = root === void 0 ? "the project's .npmrc" : join(root, ".npmrc");
7594
- const scope = scopeOf(name);
7595
- return {
7596
- package: name,
7597
- label: `the registry npm uses for ${name}`,
7598
- flags: [],
7599
- unreachableHint: scope === void 0 ? "check the network or VPN" : `check the network or VPN and the ${scope}:registry that ${npmrc} sets`,
7600
- loginHint: `log in to it (\`npm login${scope === void 0 ? "" : ` --scope=${scope}`}\`) or set its token in ${npmrc}`,
7601
- missingHint: scope === void 0 ? `check that ${name} is published on the registry ${npmrc} names` : `check that ${name} is published, and that ${npmrc} maps ${scope} to its registry: ${scope}:registry=<url>`
7602
- };
7603
- }
7604
- function adviceFor(install, root, latest) {
7605
- const packages = packagesOf(install);
7606
- const lines = root === void 0 ? void 0 : LINES[root.manager];
7607
- if (lines !== void 0) return { fix: lines.upgrade(packages) };
7608
- return { note: `move ${packages.length === 1 ? packages[0] : `${packages.slice(0, -1).join(", ")} and ${packages.at(-1)}`} to ${latest} with ${root?.manager ?? "the project's package manager"}` };
7609
- }
7610
- function checkOf$1(cli, install, runAs, { bin, node }) {
7611
- let survey;
7612
- const surveyed = (cwd) => {
7613
- survey ??= surveyDependency(install, bin, cwd);
7614
- return survey;
7615
- };
7616
- return {
7617
- install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx.cwd)),
7618
- release: async (ctx) => {
7619
- const { cwd, root, standing } = await surveyed(ctx.cwd);
7620
- if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
7621
- return {
7622
- npm: npmContext(cli, registryOf(install, root?.dir), ctx, node, root?.dir ?? cwd),
7623
- advise: (latest) => adviceFor(install, root, latest)
7624
- };
7625
- }
7626
- };
7627
- }
7628
- /** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
7629
- function dependencyInstall(cli, install) {
7630
- const runAs = runAsOf(cli);
7631
- return {
7632
- runAs,
7633
- builtIns: () => ({}),
7634
- exitClasses: [],
7635
- check: (launch) => checkOf$1(cli, install, runAs, launch),
7636
- notice: async () => {}
7637
- };
7638
- }
7639
- //#endregion
7640
- //#region ../agent-cli/dist/context/errno.js
7641
- const PERMISSION_DENIED = "PERMISSION_DENIED";
7642
- const DISK_FULL = "DISK_FULL";
7643
- const NOT_FOUND = "NOT_FOUND";
7644
- const ALREADY_EXISTS = "ALREADY_EXISTS";
7645
- const NOT_A_FILE = "NOT_A_FILE";
7646
- function permissionAdvice({ path, access, rerun, env, platform }) {
7647
- if (platform === "win32") return {
7648
- hint: `a program may hold ${path} open: close it, then run the Next step`,
7649
- next: [rerun]
7650
- };
7651
- if (!isAgent(env)) return { hint: `check the permissions of ${path} and its folder` };
7652
- return {
7653
- hint: `your sandbox may block it: ask your user to type ! then the Next step, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`,
7654
- next: [rerun]
7655
- };
7656
- }
7657
- function fileErrorOf(scope) {
7658
- return (error, at) => classifyErrno(error, {
7659
- ...at,
7660
- ...scope
7661
- });
7662
- }
7663
- function classifyErrno(error, context) {
7664
- const { path } = context;
7665
- switch (errnoOf(error)) {
7666
- case "EACCES":
7667
- case "EPERM":
7668
- case "EROFS":
7669
- case "EBUSY": return new CliError({
7670
- code: PERMISSION_DENIED,
7671
- message: `permission denied: ${path}`,
7672
- ...permissionAdvice(context)
7673
- }, { cause: error });
7674
- case "ENOSPC":
7675
- case "EDQUOT": return new CliError({
7676
- code: DISK_FULL,
7677
- message: `no space left on the disk holding ${path}`,
7678
- hint: "free space on that disk, then run the Next step",
7679
- next: [context.rerun]
7680
- }, { cause: error });
7681
- case "ENOENT":
7682
- case "ENOTDIR":
7683
- case "ENAMETOOLONG": return new CliError({
7684
- code: NOT_FOUND,
7685
- message: `not found: ${path}`,
7686
- hint: `check that ${path} and its folder exist`
7687
- }, { cause: error });
7688
- case "EEXIST": return new CliError({
7689
- code: ALREADY_EXISTS,
7690
- message: `already exists: ${path}`,
7691
- hint: `pass another path, or remove ${path}`
7692
- }, { cause: error });
7693
- case "EISDIR": return new CliError({
7694
- code: NOT_A_FILE,
7695
- message: `not a file: ${path} is a folder`,
7696
- hint: `pass the path of a file, not of the folder ${path}`
7697
- }, { cause: error });
7698
- default: return error;
7699
- }
7700
- }
7701
- //#endregion
7702
- //#region ../agent-cli/dist/context/interrupts.js
7703
- const INTERRUPTED = "INTERRUPTED";
7704
- const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
7705
- /** How long an interrupted Command may take to settle before the run ends without it. */
7706
- const GRACE_MS = 3e3;
7707
- const GRACE_OVER = Symbol("grace over");
7708
- function interruptedError(rerun) {
7709
- return new CliError({
7710
- code: INTERRUPTED,
7711
- message: "interrupted before it finished",
7712
- hint: "to finish it, run the Next step, unless it was stopped on purpose; if a tool timeout stopped it, give it longer",
7713
- next: [rerun]
7714
- });
7715
- }
7716
- const BOUND_WORDS = {
7717
- deadline: "deadline",
7718
- ignoreStop: "timeout"
7719
- };
7720
- function waitHint(bound) {
7721
- return bound === "ignoreStop" ? "the call may have been billed: ask your user before running the Next step" : "run the Next step later, when the service it waits on may answer faster";
7722
- }
7723
- function deadlineError(commandLine, ms, advice, bound = "deadline") {
7724
- return new CliError({
7725
- code: DEADLINE_EXCEEDED,
7726
- message: `${commandLine} ran past its ${ms} ms ${BOUND_WORDS[bound]}`,
7727
- ...advice
7728
- });
7729
- }
7730
- /** One Command run's cancellation: its signal, its deadlines and the steps it must finish. */
7731
- var RunControl = class {
7732
- signal;
7733
- #controller = new AbortController();
7734
- #commandLine;
7735
- #rerun;
7736
- #outer;
7737
- #onOuterAbort = () => this.#controller.abort(interruptedError(this.#rerun));
7738
- /** Each running must-finish step, tracked until it settles, with its name. */
7739
- #steps = /* @__PURE__ */ new Map();
7740
- #progress;
7741
- #clock;
7742
- #disposed = new AbortController();
7743
- /** The reasons this run's deadlines aborted with, to tell them in a Command's error. */
7744
- #abortReasons = /* @__PURE__ */ new WeakSet();
7745
- #graceCancel = new AbortController();
7746
- #endGrace = () => {};
7747
- #graceOver = new Promise((resolve) => {
7748
- this.#endGrace = resolve;
7749
- });
7750
- constructor({ commandLine, rerun, outer, clock, progress, runDeadline }) {
7751
- this.#commandLine = commandLine;
7752
- this.#rerun = rerun;
7753
- this.#outer = outer;
7754
- this.#clock = clock;
7755
- this.#progress = progress;
7756
- this.signal = this.#controller.signal;
7757
- this.signal.addEventListener("abort", () => this.#onStop(), { once: true });
7758
- if (outer?.aborted === true) this.#onOuterAbort();
7759
- else outer?.addEventListener("abort", this.#onOuterAbort, { once: true });
7760
- if (runDeadline !== void 0) {
7761
- const { ms, advice } = runDeadline;
7762
- after(clock, ms, this.#disposed.signal, () => this.#controller.abort(deadlineError(commandLine, ms, advice())));
7763
- }
7764
- }
7765
- deadline(ms, options = {}) {
7766
- return options.ignoreStop === true ? this.#abortsAfter(ms, "ignoreStop") : AbortSignal.any([this.signal, this.#abortsAfter(ms, "deadline")]);
7767
- }
7768
- #abortsAfter(ms, bound) {
7769
- const controller = new AbortController();
7770
- after(this.#clock, ms, this.#disposed.signal, () => {
7771
- const advice = {
7772
- hint: waitHint(bound),
7773
- next: [this.#rerun]
7774
- };
7775
- const reason = deadlineError(this.#commandLine, ms, advice, bound);
7776
- this.#abortReasons.add(reason);
7777
- controller.abort(reason);
7778
- });
7779
- return controller.signal;
7780
- }
7781
- async runToEnd(name, work) {
7782
- this.#graceCancel.abort();
7783
- if (this.signal.aborted) this.#finishing(name);
7784
- const step = work();
7785
- const settled = step.then(() => {}, () => {});
7786
- this.#steps.set(settled, name);
7787
- try {
7788
- return await step;
7789
- } finally {
7790
- this.#steps.delete(settled);
7791
- this.#startGrace();
7792
- }
7793
- }
7794
- /** Waits for the Command, its must-finish steps, and no longer than the grace once interrupted. */
7795
- async settle(work) {
7796
- try {
7797
- const value = await Promise.race([work, this.#graceOver]);
7798
- if (this.signal.aborted || value === GRACE_OVER) throw this.signal.reason;
7799
- return value;
7800
- } finally {
7801
- await this.#stepsDone();
7802
- this.close();
7803
- }
7804
- }
7805
- /**
7806
- * The error a run ends with: an interrupt or the run deadline outranks what the Command threw,
7807
- * unless the Command threw its own `INTERRUPTED`, saying what it finished before it stopped.
7808
- * Otherwise each leaf that is not a `CliError` and whose cause chain holds a deadline's abort is
7809
- * reported as that abort, so a declared error keeps its own code.
7810
- */
7811
- errorAfter(error) {
7812
- if (this.signal.aborted) return error instanceof CliError && error.code === "INTERRUPTED" ? error : this.signal.reason;
7813
- const leaves = errorsOf(error).map((leaf) => this.#abortIn(leaf) ?? leaf);
7814
- return leaves.length === 1 ? leaves[0] : new AggregateError(leaves);
7815
- }
7816
- #abortIn(error) {
7817
- if (error instanceof CliError) return void 0;
7818
- return causesOf(error).find((each) => each instanceof CliError && this.#abortReasons.has(each));
7819
- }
7820
- #onStop() {
7821
- for (const name of this.#steps.values()) this.#finishing(name);
7822
- this.#startGrace();
7823
- }
7824
- #finishing(name) {
7825
- this.#progress(`finishing ${name}`);
7826
- }
7827
- #startGrace() {
7828
- if (this.signal.aborted && this.#steps.size === 0) {
7829
- this.#graceCancel.abort();
7830
- this.#graceCancel = new AbortController();
7831
- after(this.#clock, GRACE_MS, this.#graceCancel.signal, () => this.#endGrace(GRACE_OVER));
8210
+ message: `${install.package} ${copy.version}, installed in ${where}`
8211
+ };
7832
8212
  }
7833
8213
  }
7834
- async #stepsDone() {
7835
- while (this.#steps.size > 0) await Promise.all(this.#steps.keys());
7836
- }
7837
- /** Stops the run's timers and its hold on the caller's signal, for a run that never settles. */
7838
- close() {
7839
- this.#graceCancel.abort();
7840
- this.#disposed.abort();
7841
- this.#outer?.removeEventListener("abort", this.#onOuterAbort);
7842
- }
7843
- };
8214
+ }
8215
+ function registryOf({ package: name }, root) {
8216
+ const npmrc = root === void 0 ? "the project's .npmrc" : join(root, ".npmrc");
8217
+ const scope = scopeOf(name);
8218
+ return {
8219
+ package: name,
8220
+ label: `the registry npm uses for ${name}`,
8221
+ flags: [],
8222
+ unreachableHint: scope === void 0 ? "check the network or VPN" : `check the network or VPN and the ${scope}:registry that ${npmrc} sets`,
8223
+ loginHint: `log in to it (\`npm login${scope === void 0 ? "" : ` --scope=${scope}`}\`) or set its token in ${npmrc}`,
8224
+ missingHint: scope === void 0 ? `check that ${name} is published on the registry ${npmrc} names` : `check that ${name} is published, and that ${npmrc} maps ${scope} to its registry: ${scope}:registry=<url>`
8225
+ };
8226
+ }
8227
+ function adviceFor(install, root, latest) {
8228
+ const packages = packagesOf(install);
8229
+ const lines = root === void 0 ? void 0 : LINES[root.manager];
8230
+ if (lines !== void 0) return { fix: lines.upgrade(packages) };
8231
+ return { note: `move ${packages.length === 1 ? packages[0] : `${packages.slice(0, -1).join(", ")} and ${packages.at(-1)}`} to ${latest} with ${root?.manager ?? "the project's package manager"}` };
8232
+ }
8233
+ function checkOf$1(cli, install, runAs, launch) {
8234
+ let survey;
8235
+ const surveyed = (ctx) => {
8236
+ survey ??= surveyDependency(install, launch.bin, ctx);
8237
+ return survey;
8238
+ };
8239
+ return {
8240
+ install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx)),
8241
+ release: async (ctx) => {
8242
+ const { cwd, root, standing } = await surveyed(ctx);
8243
+ if (isUnchecked(standing)) return { skipped: skippedFor$1(cli, install, standing) };
8244
+ return {
8245
+ npm: npmContext(cli, registryOf(install, root?.dir), ctx, launch, root?.dir ?? cwd),
8246
+ advise: (latest) => adviceFor(install, root, latest)
8247
+ };
8248
+ }
8249
+ };
8250
+ }
8251
+ /** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
8252
+ function dependencyInstall(cli, install) {
8253
+ const runAs = runAsOf(cli);
8254
+ return {
8255
+ runAs,
8256
+ builtIns: () => ({}),
8257
+ check: (launch) => checkOf$1(cli, install, runAs, launch),
8258
+ notice: async () => {}
8259
+ };
8260
+ }
7844
8261
  //#endregion
7845
8262
  //#region ../agent-cli/dist/built-ins/install/global-npm.js
7846
- const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
7847
8263
  /** Where npm puts global packages under its global prefix. */
7848
- function globalModulesDir(prefix) {
7849
- return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
8264
+ function globalModulesDir(prefix, platform) {
8265
+ return platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
8266
+ }
8267
+ /** Whether `path` itself is a symlink, which a link in a folder above it does not make it. */
8268
+ async function isLink(files, path) {
8269
+ const real = await files.realPath(path);
8270
+ if (real === void 0) return false;
8271
+ const folder = await files.realPath(dirname(path));
8272
+ return folder === void 0 || real !== join(folder, basename(path));
7850
8273
  }
7851
8274
  function notGlobal(ctx, message) {
7852
8275
  return runtimeError({
@@ -7863,11 +8286,11 @@ function notGlobal(ctx, message) {
7863
8286
  async function globalInstallOf(bin, ctx) {
7864
8287
  if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
7865
8288
  const prefix = await globalPrefix(ctx);
7866
- const packageDir = join(globalModulesDir(prefix), ctx.registry.package);
7867
- const installed = await orMissing(realpath(packageDir));
7868
- const here = await orMissing(realpath(bin)) ?? bin;
8289
+ const packageDir = join(globalModulesDir(prefix, ctx.platform), ctx.registry.package);
8290
+ const installed = await ctx.files.realPath(packageDir);
8291
+ const here = await ctx.files.realPath(bin) ?? bin;
7869
8292
  if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
7870
- if ((await lstat(packageDir)).isSymbolicLink()) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
8293
+ if (await isLink(ctx.files, packageDir)) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
7871
8294
  return {
7872
8295
  prefix,
7873
8296
  packageDir
@@ -7896,25 +8319,32 @@ function globalRegistry(install) {
7896
8319
  };
7897
8320
  }
7898
8321
  /** npm for a Global install, run in the working directory. */
7899
- function globalNpm(cli, install, ctx, node) {
7900
- return npmContext(cli, globalRegistry(install), ctx, node, ctx.cwd);
8322
+ function globalNpm(cli, install, ctx, launch) {
8323
+ return npmContext(cli, globalRegistry(install), ctx, launch, ctx.cwd);
7901
8324
  }
7902
8325
  //#endregion
7903
8326
  //#region ../agent-cli/dist/built-ins/skill/sync.js
7904
- /** Every file under `dir`, relative to it and sorted; none when `dir` is missing. */
7905
- async function filesIn(dir) {
7906
- return (await orMissing(readdir(dir, {
7907
- recursive: true,
7908
- withFileTypes: true
7909
- })) ?? []).filter((entry) => entry.isFile()).map((entry) => relative(dir, join(entry.parentPath, entry.name))).sort();
7910
- }
7911
- async function sameContent(left, right) {
7912
- const [a, b] = await Promise.all([readFile(left), readFile(right)]);
7913
- return a.equals(b);
7914
- }
7915
- async function syncOf(source, copy) {
7916
- const [shipped, present] = await Promise.all([filesIn(source), filesIn(copy)]);
7917
- const differs = await Promise.all(shipped.map(async (file) => present.includes(file) && !await sameContent(join(source, file), join(copy, file))));
8327
+ async function walk$1(files, dir, inner) {
8328
+ const names = await files.list(join(dir, inner));
8329
+ return (await Promise.all(names.map(async (name) => {
8330
+ const path = join(inner, name);
8331
+ const found = await files.stat(join(dir, path));
8332
+ if (found === void 0) return [];
8333
+ return found.folder ? walk$1(files, dir, path) : [path];
8334
+ }))).flat();
8335
+ }
8336
+ /** Every file under `dir`, relative to it and sorted; none when `dir` is no folder. */
8337
+ async function filesIn(files, dir) {
8338
+ if ((await files.stat(dir))?.folder !== true) return [];
8339
+ return (await walk$1(files, dir, "")).sort();
8340
+ }
8341
+ async function sameContent(files, left, right) {
8342
+ const [a, b] = await Promise.all([files.read(left), files.read(right)]);
8343
+ return Buffer.from(a).equals(b);
8344
+ }
8345
+ async function syncOf(files, source, copy) {
8346
+ const [shipped, present] = await Promise.all([filesIn(files, source), filesIn(files, copy)]);
8347
+ const differs = await Promise.all(shipped.map(async (file) => present.includes(file) && !await sameContent(files, join(source, file), join(copy, file))));
7918
8348
  return {
7919
8349
  source,
7920
8350
  copy,
@@ -7924,7 +8354,7 @@ async function syncOf(source, copy) {
7924
8354
  };
7925
8355
  }
7926
8356
  async function applySync(sync, ctx) {
7927
- for (const file of [...sync.write, ...sync.replace]) await ctx.files.write(join(sync.copy, file), await readFile(join(sync.source, file)));
8357
+ for (const file of [...sync.write, ...sync.replace]) await ctx.files.write(join(sync.copy, file), await ctx.files.read(join(sync.source, file)));
7928
8358
  for (const file of sync.remove) await ctx.files.remove(join(sync.copy, file));
7929
8359
  }
7930
8360
  //#endregion
@@ -7945,9 +8375,9 @@ function homeOf(env) {
7945
8375
  return env.HOME ?? env.USERPROFILE ?? homedir();
7946
8376
  }
7947
8377
  /** The home folders of the Skill copy; the skills installer may have made one a link to the other. */
7948
- async function homeCopiesOf(name, env) {
8378
+ async function homeCopiesOf(files, name, env) {
7949
8379
  const paths = SKILL_DIRS.map((dir) => join(homeOf(env), ...dir, name));
7950
- const reals = await Promise.all(paths.map(async (path) => orMissing(realpath(path))));
8380
+ const reals = await Promise.all(paths.map((path) => files.realPath(path)));
7951
8381
  return paths.filter((_path, index) => {
7952
8382
  const real = reals[index];
7953
8383
  return real === void 0 || reals.indexOf(real) === index;
@@ -7965,9 +8395,9 @@ const homeSyncSchema = object({
7965
8395
  async function syncHome(cli, packageDir, ctx) {
7966
8396
  if (cli.skill === void 0) return null;
7967
8397
  const source = join(packageDir, cli.skill.folder);
7968
- if ((await filesIn(source)).length === 0) return null;
7969
- const copies = await homeCopiesOf(cli.name, ctx.env);
7970
- const syncs = await Promise.all(copies.map((copy) => syncOf(source, copy)));
8398
+ if ((await filesIn(ctx.files, source)).length === 0) return null;
8399
+ const copies = await homeCopiesOf(ctx.files, cli.name, ctx.env);
8400
+ const syncs = await Promise.all(copies.map((copy) => syncOf(ctx.files, source, copy)));
7971
8401
  for (const sync of syncs) await applySync(sync, ctx);
7972
8402
  return {
7973
8403
  copies,
@@ -7984,19 +8414,25 @@ function renderHomeSync(name, sync) {
7984
8414
  const at = sync.copies.join(", ");
7985
8415
  return sync.written.length + sync.removed.length === 0 ? [`the ${name} skill at ${at} already matches the package`] : [`copied the ${name} skill to ${at}: wrote ${sync.written.length} files, removed ${sync.removed.length}`];
7986
8416
  }
7987
- async function homeCopyAt(path) {
8417
+ async function homeCopyAt(files, path) {
8418
+ let text;
7988
8419
  try {
7989
- return {
7990
- path,
7991
- state: "readable",
7992
- stated: skillVersionIn(await readFile(join(path, "SKILL.md"), "utf8"))
7993
- };
8420
+ text = await readIfAny(files, join(path, "SKILL.md"));
7994
8421
  } catch (error) {
8422
+ if (!(error instanceof CliError)) throw error;
7995
8423
  return {
7996
8424
  path,
7997
- state: isMissingFile(error) ? "missing" : "unreadable"
8425
+ state: "unreadable"
7998
8426
  };
7999
8427
  }
8428
+ return text === void 0 ? {
8429
+ path,
8430
+ state: "missing"
8431
+ } : {
8432
+ path,
8433
+ state: "readable",
8434
+ stated: skillVersionIn(text)
8435
+ };
8000
8436
  }
8001
8437
  function describeCopy(copy) {
8002
8438
  if (copy.state === "missing") return `no copy at ${copy.path}`;
@@ -8004,8 +8440,8 @@ function describeCopy(copy) {
8004
8440
  return copy.stated === void 0 ? copy.path : `${copy.path} (${statedVersionNote(copy.stated)})`;
8005
8441
  }
8006
8442
  async function findingOf$1(cli, folder, install, ctx) {
8007
- const paths = await homeCopiesOf(cli.name, ctx.env);
8008
- const [copies, shipped] = await Promise.all([Promise.all(paths.map(homeCopyAt)), orMissing(readFile(join(ctx.packageRoot, folder, "SKILL.md"), "utf8"))]);
8443
+ const paths = await homeCopiesOf(ctx.files, cli.name, ctx.env);
8444
+ const [copies, shipped] = await Promise.all([Promise.all(paths.map((path) => homeCopyAt(ctx.files, path))), readIfAny(ctx.files, join(ctx.packageRoot, folder, "SKILL.md"))]);
8009
8445
  const message = copies.map(describeCopy).join("; ");
8010
8446
  const shipsStated = shipped !== void 0 && skillVersionIn(shipped) !== void 0;
8011
8447
  return copies.every((copy) => copy.state === "readable" && (copy.stated === void 0 || shipsStated)) ? {
@@ -8083,14 +8519,9 @@ function upgradeOf({ version, lines }) {
8083
8519
  * What each release after `from` up to `to` asks of the operator, oldest first, from the
8084
8520
  * CHANGELOG.md in `packageDir`; none when the package ships no CHANGELOG.
8085
8521
  */
8086
- async function upgradesOf(packageDir, from, to) {
8087
- let changelog;
8088
- try {
8089
- changelog = await readFile(join(packageDir, "CHANGELOG.md"), "utf8");
8090
- } catch (error) {
8091
- if (isMissingFile(error)) return [];
8092
- throw error;
8093
- }
8522
+ async function upgradesOf(files, packageDir, from, to) {
8523
+ const changelog = await readIfAny(files, join(packageDir, "CHANGELOG.md"));
8524
+ if (changelog === void 0) return [];
8094
8525
  return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version)).flatMap(upgradeOf);
8095
8526
  }
8096
8527
  function renderUpgrade(upgrade) {
@@ -8098,7 +8529,6 @@ function renderUpgrade(upgrade) {
8098
8529
  }
8099
8530
  //#endregion
8100
8531
  //#region ../agent-cli/dist/built-ins/setup.js
8101
- const NOT_ON_PATH = "NOT_ON_PATH";
8102
8532
  const setupSchema = object({
8103
8533
  version: string(),
8104
8534
  prefix: string(),
@@ -8108,8 +8538,8 @@ const setupSchema = object({
8108
8538
  docs: string().nullable()
8109
8539
  });
8110
8540
  const installedManifest = object({ version: string() });
8111
- async function versionAt(packageDir) {
8112
- const text = await orMissing(readFile(join(packageDir, "package.json"), "utf8"));
8541
+ async function versionAt(files, packageDir) {
8542
+ const text = await readIfAny(files, join(packageDir, "package.json"));
8113
8543
  if (text === void 0) return null;
8114
8544
  try {
8115
8545
  const parsed = installedManifest.safeParse(JSON.parse(text));
@@ -8119,26 +8549,29 @@ async function versionAt(packageDir) {
8119
8549
  throw error;
8120
8550
  }
8121
8551
  }
8122
- function npmOfSetup(cli, install, ctx, node) {
8552
+ function npmOfSetup(cli, install, ctx, launch) {
8123
8553
  return {
8124
- ...globalNpm(cli, install, ctx, node),
8554
+ ...globalNpm(cli, install, ctx, launch),
8125
8555
  rerun: wholeLine(setupLineOf(install))
8126
8556
  };
8127
8557
  }
8128
8558
  function needsInstall({ from, latest }) {
8129
8559
  return from === null || standingOf$1(from, latest) === "behind";
8130
8560
  }
8131
- function binDirOf(prefix) {
8132
- return process.platform === "win32" ? prefix : join(prefix, "bin");
8561
+ function binDirOf(prefix, platform) {
8562
+ return platform === "win32" ? prefix : join(prefix, "bin");
8133
8563
  }
8134
- async function canonical(path) {
8135
- return normalize(await realpath(path).catch(() => path));
8564
+ async function canonical(files, path) {
8565
+ const real = await files.realPath(path).catch((error) => {
8566
+ if (!(error instanceof CliError) && errnoOf(error) === void 0) throw error;
8567
+ });
8568
+ return normalize(real ?? path);
8136
8569
  }
8137
- async function assertOnPath(cli, prefix, node, ctx) {
8138
- const binDir = binDirOf(prefix);
8139
- const wanted = await canonical(binDir);
8570
+ async function assertOnPath(cli, prefix, { node, platform }, ctx) {
8571
+ const binDir = binDirOf(prefix, platform);
8572
+ const wanted = await canonical(ctx.files, binDir);
8140
8573
  const entries = pathOf(ctx.env).split(delimiter).filter(Boolean);
8141
- if ((await Promise.all(entries.map(canonical))).includes(wanted)) return;
8574
+ if ((await Promise.all(entries.map((entry) => canonical(ctx.files, entry)))).includes(wanted)) return;
8142
8575
  throw runtimeError({
8143
8576
  code: NOT_ON_PATH,
8144
8577
  message: `${cli.name} is installed in ${binDir}, by the npm of ${node ?? "the Node on PATH"}, a folder not on this shell's PATH`,
@@ -8170,7 +8603,7 @@ function renderSetup(cli, install, setup) {
8170
8603
  ].join("\n");
8171
8604
  }
8172
8605
  /** The built-in `setup`, which runs from any copy, such as npx's, so it never asks to be the global one. */
8173
- function setupCommand(cli, install, { node }) {
8606
+ function setupCommand(cli, install, launch) {
8174
8607
  return defineCommand$1({
8175
8608
  description: "install this CLI and its skill on this machine",
8176
8609
  examples: [{
@@ -8181,12 +8614,17 @@ function setupCommand(cli, install, { node }) {
8181
8614
  note: "show what it would change"
8182
8615
  }],
8183
8616
  effect: "write",
8617
+ errors: [
8618
+ NOT_GLOBAL_INSTALL,
8619
+ NOT_ON_PATH,
8620
+ ...NPM_ERRORS
8621
+ ],
8184
8622
  output: setupSchema,
8185
8623
  plan: async (_input, ctx) => {
8186
- const npm = npmOfSetup(cli, install, ctx, node);
8624
+ const npm = npmOfSetup(cli, install, ctx, launch);
8187
8625
  const prefix = await globalPrefix(npm);
8188
- const packageDir = join(globalModulesDir(prefix), install.package);
8189
- if ((await orMissing(lstat(packageDir)))?.isSymbolicLink() === true) throw runtimeError({
8626
+ const packageDir = join(globalModulesDir(prefix, launch.platform), install.package);
8627
+ if (await isLink(ctx.files, packageDir)) throw runtimeError({
8190
8628
  code: NOT_GLOBAL_INSTALL,
8191
8629
  message: `${packageDir} is a link (npm link), which an install would replace`,
8192
8630
  hint: "remove the link with npm unlink, then run the Next step",
@@ -8194,11 +8632,11 @@ function setupCommand(cli, install, { node }) {
8194
8632
  });
8195
8633
  return {
8196
8634
  package: install.package,
8197
- from: await versionAt(packageDir),
8635
+ from: await versionAt(ctx.files, packageDir),
8198
8636
  latest: await latestVersion(npm),
8199
8637
  prefix,
8200
8638
  packageDir,
8201
- copies: cli.skill === void 0 ? [] : await homeCopiesOf(cli.name, ctx.env)
8639
+ copies: cli.skill === void 0 ? [] : await homeCopiesOf(ctx.files, cli.name, ctx.env)
8202
8640
  };
8203
8641
  },
8204
8642
  apply: async (plan, ctx) => {
@@ -8207,7 +8645,7 @@ function setupCommand(cli, install, { node }) {
8207
8645
  const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
8208
8646
  if (installs) {
8209
8647
  ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
8210
- await installGlobally(latest, npmOfSetup(cli, install, ctx, node));
8648
+ await installGlobally(latest, npmOfSetup(cli, install, ctx, launch));
8211
8649
  }
8212
8650
  return syncHome(cli, packageDir, ctx);
8213
8651
  }).catch((error) => {
@@ -8218,7 +8656,7 @@ function setupCommand(cli, install, { node }) {
8218
8656
  prefix,
8219
8657
  installed: installs,
8220
8658
  skill,
8221
- upgrades: installs && from !== null ? await upgradesOf(packageDir, from, latest) : [],
8659
+ upgrades: installs && from !== null ? await upgradesOf(ctx.files, packageDir, from, latest) : [],
8222
8660
  docs: cli.docs === void 0 ? null : join(packageDir, cli.docs)
8223
8661
  };
8224
8662
  ctx.deliver(setup);
@@ -8227,7 +8665,7 @@ function setupCommand(cli, install, { node }) {
8227
8665
  message: `interrupted, but ${plan.package}@${setup.version} and its skill are already in place`,
8228
8666
  hint: "nothing to run again"
8229
8667
  });
8230
- await assertOnPath(cli, prefix, node, ctx);
8668
+ await assertOnPath(cli, prefix, launch, ctx);
8231
8669
  return setup;
8232
8670
  },
8233
8671
  render: (setup) => renderSetup(cli, install, setup),
@@ -8266,7 +8704,7 @@ function interruptedAfterInstall(plan) {
8266
8704
  });
8267
8705
  }
8268
8706
  /** The built-in `update`, for a CLI installed globally with npm. */
8269
- function updateCommand(cli, install, { bin, node }) {
8707
+ function updateCommand(cli, install, launch) {
8270
8708
  return defineCommand$1({
8271
8709
  description: "install the latest version of this CLI from its registry",
8272
8710
  examples: [{
@@ -8277,17 +8715,18 @@ function updateCommand(cli, install, { bin, node }) {
8277
8715
  note: "show the version it would install"
8278
8716
  }],
8279
8717
  effect: "write",
8718
+ errors: [NOT_GLOBAL_INSTALL, ...NPM_ERRORS],
8280
8719
  output: updateSchema,
8281
8720
  plan: async (_input, ctx) => {
8282
- const npm = globalNpm(cli, install, ctx, node);
8283
- const copy = await globalInstallOf(bin, npm);
8721
+ const npm = globalNpm(cli, install, ctx, launch);
8722
+ const copy = await globalInstallOf(launch.bin, npm);
8284
8723
  return {
8285
8724
  package: install.package,
8286
8725
  from: cli.version,
8287
8726
  latest: await latestVersion(npm),
8288
8727
  prefix: copy.prefix,
8289
8728
  packageDir: copy.packageDir,
8290
- copies: cli.skill === void 0 ? [] : await homeCopiesOf(cli.name, ctx.env)
8729
+ copies: cli.skill === void 0 ? [] : await homeCopiesOf(ctx.files, cli.name, ctx.env)
8291
8730
  };
8292
8731
  },
8293
8732
  apply: async (plan, ctx) => {
@@ -8298,205 +8737,32 @@ function updateCommand(cli, install, { bin, node }) {
8298
8737
  installed: null,
8299
8738
  skill: await syncHome(cli, plan.packageDir, ctx),
8300
8739
  upgrades: []
8301
- };
8302
- ctx.signal.throwIfAborted();
8303
- ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
8304
- const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
8305
- await installGlobally(latest, globalNpm(cli, install, ctx, node));
8306
- return syncHome(cli, plan.packageDir, ctx);
8307
- });
8308
- const update = {
8309
- from,
8310
- latest,
8311
- installed: {
8312
- version: latest,
8313
- prefix
8314
- },
8315
- skill,
8316
- upgrades: await upgradesOf(plan.packageDir, from, latest)
8317
- };
8318
- if (ctx.signal.aborted) {
8319
- ctx.deliver(update);
8320
- throw interruptedAfterInstall(plan);
8321
- }
8322
- return update;
8323
- },
8324
- render: (update) => renderUpdate(cli, install, update),
8325
- renderPlan: (plan) => [standingOf$1(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan), ...renderPlannedSync(cli.name, plan.copies)].join("\n")
8326
- });
8327
- }
8328
- //#endregion
8329
- //#region ../agent-cli/dist/context/files.js
8330
- const TEMP_FILE = /^\..+\.agent-cli-(\d+)\.tmp$/;
8331
- /** Where the Runtime writes `path` before moving it into place; a dead writer's is swept. */
8332
- function tempPathOf(path) {
8333
- return join(dirname(path), `.${basename(path)}.agent-cli-${process.pid}.tmp`);
8334
- }
8335
- function isAlive(pid) {
8336
- try {
8337
- process.kill(pid, 0);
8338
- return true;
8339
- } catch (error) {
8340
- return errnoOf(error) === "EPERM";
8341
- }
8342
- }
8343
- async function sweep(folder) {
8344
- const dead = (await readdir(folder)).filter((name) => {
8345
- const pid = TEMP_FILE.exec(name)?.[1];
8346
- return pid !== void 0 && !isAlive(Number(pid));
8347
- });
8348
- await Promise.all(dead.map((name) => rm(join(folder, name), { force: true })));
8349
- }
8350
- /** The file a symlink at `path` points to, so a replacing write keeps the link. */
8351
- async function targetOf$1(path) {
8352
- try {
8353
- return await realpath(path);
8354
- } catch (error) {
8355
- if (isMissingFile(error)) return path;
8356
- throw error;
8357
- }
8358
- }
8359
- async function writeWithMode(path, data, mode) {
8360
- if (mode === void 0) {
8361
- await writeFile(path, data);
8362
- return;
8363
- }
8364
- await writeFile(path, data, { mode });
8365
- await chmod(path, mode);
8366
- }
8367
- /** What `link` answers on a file system without hard links, such as exFAT. */
8368
- const NO_HARD_LINKS = /* @__PURE__ */ new Set([
8369
- "EPERM",
8370
- "ENOTSUP",
8371
- "EXDEV"
8372
- ]);
8373
- /** A link fails atomically when `path` is taken, so no reader ever sees half a file there. */
8374
- async function linkNew(temp, path, data, mode) {
8375
- try {
8376
- await link(temp, path);
8377
- } catch (error) {
8378
- if (!NO_HARD_LINKS.has(errnoOf(error))) throw error;
8379
- await createNew(path, data, mode);
8380
- }
8381
- }
8382
- /** A file this call created and could not finish is removed, so a rerun does not find half of it. */
8383
- async function createNew(path, data, mode) {
8384
- const handle = await open(path, "wx", mode);
8385
- try {
8386
- await handle.writeFile(data);
8387
- if (mode !== void 0) await handle.chmod(mode);
8388
- } catch (error) {
8389
- await handle.close();
8390
- await rm(path, { force: true });
8391
- throw error;
8392
- }
8393
- await handle.close();
8394
- }
8395
- const RENAME_ATTEMPTS = 10;
8396
- const RENAME_BACKOFF_MS = 10;
8397
- const HELD_BY_ANOTHER_PROGRAM = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
8398
- async function renameRetrying(from, to) {
8399
- for (let attempt = 1;; attempt++) try {
8400
- await rename(from, to);
8401
- return;
8402
- } catch (error) {
8403
- if (!(process.platform === "win32" && HELD_BY_ANOTHER_PROGRAM.has(errnoOf(error))) || attempt === RENAME_ATTEMPTS) throw error;
8404
- await setTimeout$1(attempt * RENAME_BACKOFF_MS);
8405
- }
8406
- }
8407
- /** Real files for one run: the first write to each folder sweeps the temp files of dead writers. */
8408
- function realFiles() {
8409
- const swept = /* @__PURE__ */ new Set();
8410
- return {
8411
- exists: async (path) => {
8412
- try {
8413
- await access(path);
8414
- return true;
8415
- } catch (error) {
8416
- if (isNothingThere(error)) return false;
8417
- throw error;
8418
- }
8419
- },
8420
- write: async (path, data, options = {}) => {
8421
- await mkdir(dirname(path), { recursive: true });
8422
- const target = options.exclusive === true ? path : await targetOf$1(path);
8423
- const folder = dirname(target);
8424
- if (!swept.has(folder)) {
8425
- await sweep(folder);
8426
- swept.add(folder);
8427
- }
8428
- const temp = tempPathOf(target);
8429
- try {
8430
- await writeWithMode(temp, data, options.mode);
8431
- if (options.exclusive === true) {
8432
- await linkNew(temp, target, data, options.mode);
8433
- await rm(temp);
8434
- } else await renameRetrying(temp, target);
8435
- } catch (error) {
8436
- await rm(temp, { force: true });
8437
- throw error;
8438
- }
8439
- },
8440
- read: (path) => readFile(path),
8441
- list: (folder) => readdir(folder),
8442
- remove: (path) => rm(path, {
8443
- recursive: true,
8444
- force: true
8445
- }),
8446
- mkdir: async (path, options) => {
8447
- const mode = options?.mode;
8448
- if (options?.exclusive !== true) {
8449
- await mkdir(path, {
8450
- recursive: true,
8451
- mode
8452
- });
8453
- return;
8454
- }
8455
- await mkdir(dirname(path), {
8456
- recursive: true,
8457
- mode
8458
- });
8459
- await mkdir(path, { mode });
8460
- },
8461
- stat: async (path) => {
8462
- try {
8463
- const { size, dev, ino } = await stat(path, { bigint: true });
8464
- return {
8465
- size: Number(size),
8466
- id: `${dev}:${ino}`
8467
- };
8468
- } catch (error) {
8469
- if (isNothingThere(error)) return void 0;
8470
- throw error;
8471
- }
8472
- }
8473
- };
8474
- }
8475
- /**
8476
- * `files` resolving paths against `cwd`, its failures classified by errno; `list` drops the
8477
- * Runtime's temp files and sorts.
8478
- */
8479
- function classifiedFiles(files, { cwd, fileError }) {
8480
- async function at(access, path, work) {
8481
- const target = resolve(cwd, path);
8482
- try {
8483
- return await work(target);
8484
- } catch (error) {
8485
- throw fileError(error, {
8486
- path: target,
8487
- access
8740
+ };
8741
+ ctx.signal.throwIfAborted();
8742
+ ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
8743
+ const skill = await ctx.runToEnd(`installing ${plan.package}@${latest}`, async () => {
8744
+ await installGlobally(latest, globalNpm(cli, install, ctx, launch));
8745
+ return syncHome(cli, plan.packageDir, ctx);
8488
8746
  });
8489
- }
8490
- }
8491
- return {
8492
- exists: (path) => at("read", path, (target) => files.exists(target)),
8493
- write: (path, data, options) => at("write", path, (target) => files.write(target, data, options)),
8494
- read: (path) => at("read", path, (target) => files.read(target)),
8495
- list: (folder) => at("read", folder, async (target) => (await files.list(target)).filter((name) => !TEMP_FILE.test(name)).sort()),
8496
- remove: (path) => at("write", path, (target) => files.remove(target)),
8497
- mkdir: (path, options) => at("write", path, (target) => files.mkdir(target, options)),
8498
- stat: (path) => at("read", path, (target) => files.stat(target))
8499
- };
8747
+ const update = {
8748
+ from,
8749
+ latest,
8750
+ installed: {
8751
+ version: latest,
8752
+ prefix
8753
+ },
8754
+ skill,
8755
+ upgrades: await upgradesOf(ctx.files, plan.packageDir, from, latest)
8756
+ };
8757
+ if (ctx.signal.aborted) {
8758
+ ctx.deliver(update);
8759
+ throw interruptedAfterInstall(plan);
8760
+ }
8761
+ return update;
8762
+ },
8763
+ render: (update) => renderUpdate(cli, install, update),
8764
+ renderPlan: (plan) => [standingOf$1(plan.from, plan.latest) === "behind" ? `would install ${plan.package}@${plan.latest} with the npm under ${plan.prefix}, replacing ${plan.from}` : nothingToInstall(plan), ...renderPlannedSync(cli.name, plan.copies)].join("\n")
8765
+ });
8500
8766
  }
8501
8767
  //#endregion
8502
8768
  //#region ../agent-cli/dist/built-ins/install/release-check.js
@@ -8561,25 +8827,19 @@ function parsedCheck(text) {
8561
8827
  return;
8562
8828
  }
8563
8829
  }
8564
- async function lastCheck(file) {
8565
- const text = await orMissing(readFile(file, "utf8"));
8830
+ async function lastCheck(files, file) {
8831
+ const text = await readIfAny(files, file);
8566
8832
  return text === void 0 ? void 0 : parsedCheck(text);
8567
8833
  }
8568
- async function writeCheck(file, check) {
8569
- await mkdir(dirname(file), { recursive: true });
8570
- const temp = tempPathOf(file);
8571
- await writeFile(temp, JSON.stringify(check));
8572
- await rename(temp, file);
8573
- }
8574
- async function startCheck(cli, install, file, last, { node }, ctx) {
8834
+ async function startCheck(cli, install, file, last, launch, ctx) {
8575
8835
  const checkedAt = ctx.clock.now();
8576
8836
  const previous = last?.latest ?? "";
8577
- await writeCheck(file, {
8837
+ await ctx.files.write(file, JSON.stringify({
8578
8838
  checkedAt,
8579
8839
  latest: last?.latest
8580
- });
8581
- const npm = globalNpm(cli, install, ctx, node);
8582
- const { path, env } = npmOf(npm, process.platform === "win32");
8840
+ }));
8841
+ const npm = globalNpm(cli, install, ctx, launch);
8842
+ const { path, env } = await npmOf(npm);
8583
8843
  await ctx.spawnDetached(process.execPath, [
8584
8844
  "-e",
8585
8845
  CHECK_SCRIPT,
@@ -8612,7 +8872,7 @@ async function releaseCheck(cli, install, runAs, launch, ctx) {
8612
8872
  }
8613
8873
  async function checkAndTell(cli, install, runAs, launch, ctx) {
8614
8874
  const file = cacheFileOf(cli.name, ctx.env);
8615
- const last = await lastCheck(file);
8875
+ const last = await lastCheck(ctx.files, file);
8616
8876
  if (last?.error !== void 0) debugLine(cli, ctx, last.error);
8617
8877
  if (last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS) await startCheck(cli, install, file, last, launch, ctx);
8618
8878
  const latest = last?.latest;
@@ -8621,14 +8881,14 @@ async function checkAndTell(cli, install, runAs, launch, ctx) {
8621
8881
  }
8622
8882
  //#endregion
8623
8883
  //#region ../agent-cli/dist/built-ins/install/global.js
8624
- function checkOf(cli, install, { bin, node }) {
8884
+ function checkOf(cli, install, launch) {
8625
8885
  return {
8626
8886
  install: async (ctx) => {
8627
- const npm = globalNpm(cli, install, ctx, node);
8887
+ const npm = globalNpm(cli, install, ctx, launch);
8628
8888
  try {
8629
8889
  return {
8630
8890
  state: "ok",
8631
- message: `a global npm install under ${(await globalInstallOf(bin, npm)).prefix}`
8891
+ message: `a global npm install under ${(await globalInstallOf(launch.bin, npm)).prefix}`
8632
8892
  };
8633
8893
  } catch (error) {
8634
8894
  if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
@@ -8642,7 +8902,7 @@ function checkOf(cli, install, { bin, node }) {
8642
8902
  release: async (ctx) => {
8643
8903
  const fix = `${cli.name} update`;
8644
8904
  return {
8645
- npm: globalNpm(cli, install, ctx, node),
8905
+ npm: globalNpm(cli, install, ctx, launch),
8646
8906
  advise: () => ({ fix })
8647
8907
  };
8648
8908
  }
@@ -8656,7 +8916,6 @@ function globalInstall(cli, install) {
8656
8916
  setup: setupCommand(cli, install, launch),
8657
8917
  update: updateCommand(cli, install, launch)
8658
8918
  }),
8659
- exitClasses: ["source"],
8660
8919
  check: (launch) => checkOf(cli, install, launch),
8661
8920
  notice: (launch, ctx) => releaseCheck(cli, install, adapter.runAs, launch, ctx)
8662
8921
  };
@@ -8673,7 +8932,6 @@ function noInstall(cli) {
8673
8932
  return {
8674
8933
  runAs: runAsOf(cli),
8675
8934
  builtIns: () => ({}),
8676
- exitClasses: [],
8677
8935
  check: () => ({
8678
8936
  install: skipped,
8679
8937
  release: async () => ({ skipped: await skipped() })
@@ -8704,9 +8962,9 @@ function withSection({ name, text: section }, text) {
8704
8962
  if (text === "") return `${whole}\n`;
8705
8963
  return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
8706
8964
  }
8707
- async function agentsMdAt(agents, root) {
8965
+ async function agentsMdAt(files, agents, root) {
8708
8966
  const path = join(root, "AGENTS.md");
8709
- const existing = await orMissing(readFile(path, "utf8"));
8967
+ const existing = await readIfAny(files, path);
8710
8968
  if (existing === void 0) return {
8711
8969
  path,
8712
8970
  agents,
@@ -8723,9 +8981,9 @@ async function agentsMdAt(agents, root) {
8723
8981
  change: existing.includes(markersOf(agents.name).begin) ? "rewrite" : "add"
8724
8982
  };
8725
8983
  }
8726
- async function handEditsAt(root) {
8984
+ async function handEditsAt(files, root) {
8727
8985
  const claudeMd = join(root, "CLAUDE.md");
8728
- const text = await orMissing(readFile(claudeMd, "utf8"));
8986
+ const text = await readIfAny(files, claudeMd);
8729
8987
  if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
8730
8988
  return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
8731
8989
  }
@@ -8735,8 +8993,8 @@ function agentsRow(agents, install) {
8735
8993
  name: "agents",
8736
8994
  advisory: true,
8737
8995
  run: async (ctx) => {
8738
- const root = await projectRootOf(ctx.cwd);
8739
- const { path, change } = await agentsMdAt(agents, root);
8996
+ const root = await projectRootOf(ctx.files, ctx.cwd);
8997
+ const { path, change } = await agentsMdAt(ctx.files, agents, root);
8740
8998
  const fix = `${install.runAs} skill install`;
8741
8999
  if (change === "rewrite") return {
8742
9000
  state: "problem",
@@ -8749,7 +9007,7 @@ function agentsRow(agents, install) {
8749
9007
  fix
8750
9008
  };
8751
9009
  const holds = `${path} holds the ${name} Agents section`;
8752
- const [edit] = await handEditsAt(root);
9010
+ const [edit] = await handEditsAt(ctx.files, root);
8753
9011
  return edit === void 0 ? {
8754
9012
  state: "ok",
8755
9013
  message: holds
@@ -8763,8 +9021,8 @@ function agentsRow(agents, install) {
8763
9021
  //#endregion
8764
9022
  //#region ../agent-cli/dist/built-ins/skill/install-command.js
8765
9023
  /** The project root and the Skill copy under it, as `skill install` writes it. */
8766
- async function skillCopyOf(cli, cwd) {
8767
- const root = await projectRootOf(cwd);
9024
+ async function skillCopyOf(files, cli, cwd) {
9025
+ const root = await projectRootOf(files, cwd);
8768
9026
  return {
8769
9027
  root,
8770
9028
  copy: join(root, ".claude", "skills", cli.name)
@@ -8780,12 +9038,14 @@ const installSchema = object({
8780
9038
  }).nullable(),
8781
9039
  handEdits: array(string())
8782
9040
  });
8783
- async function planOf(cli, folder, agents, cwd, packageRoot) {
8784
- const { root, copy } = await skillCopyOf(cli, cwd);
9041
+ async function planOf(cli, folder, agents, { files, cwd, packageRoot }) {
9042
+ const source = join(packageRoot, folder);
9043
+ await readRequired(files, join(source, "SKILL.md"));
9044
+ const { root, copy } = await skillCopyOf(files, cli, cwd);
8785
9045
  return {
8786
- ...await syncOf(join(packageRoot, folder), copy),
8787
- agentsMd: agents === void 0 ? null : await agentsMdAt(agents, root),
8788
- handEdits: agents === void 0 ? [] : await handEditsAt(root)
9046
+ ...await syncOf(files, source, copy),
9047
+ agentsMd: agents === void 0 ? null : await agentsMdAt(files, agents, root),
9048
+ handEdits: agents === void 0 ? [] : await handEditsAt(files, root)
8789
9049
  };
8790
9050
  }
8791
9051
  const SECTION_VERBS = {
@@ -8828,12 +9088,12 @@ function skillInstallCommand(cli, folder, agents) {
8828
9088
  }],
8829
9089
  effect: "write",
8830
9090
  output: installSchema,
8831
- plan: (_input, ctx) => planOf(cli, folder, agents, ctx.cwd, ctx.packageRoot),
9091
+ plan: (_input, ctx) => planOf(cli, folder, agents, ctx),
8832
9092
  apply: async (plan, ctx) => {
8833
9093
  await applySync(plan, ctx);
8834
9094
  const { agentsMd } = plan;
8835
9095
  if (agentsMd !== null && agentsMd.change !== null) {
8836
- const existing = await orMissing(readFile(agentsMd.path, "utf8")) ?? "";
9096
+ const existing = await readIfAny(ctx.files, agentsMd.path) ?? "";
8837
9097
  await ctx.files.write(agentsMd.path, withSection(agentsMd.agents, existing));
8838
9098
  }
8839
9099
  return {
@@ -8855,13 +9115,13 @@ function skillInstallCommand(cli, folder, agents) {
8855
9115
  //#region ../agent-cli/dist/built-ins/skill/project.js
8856
9116
  async function personalCopyOf(name, copy, ctx) {
8857
9117
  const home = join(homeOf(ctx.env), ".claude", "skills", name);
8858
- const real = await orMissing(realpath(home));
8859
- return real === void 0 || real === await orMissing(realpath(copy)) ? void 0 : home;
9118
+ const real = await ctx.files.realPath(home);
9119
+ return real === void 0 || real === await ctx.files.realPath(copy) ? void 0 : home;
8860
9120
  }
8861
9121
  /** The project root's copy against the package's, and any under home. */
8862
9122
  async function findingOf(cli, folder, install, ctx) {
8863
- const { copy } = await skillCopyOf(cli, ctx.cwd);
8864
- const [installed, shipped] = await Promise.all([orMissing(readFile(join(copy, "SKILL.md"), "utf8")), readFile(join(ctx.packageRoot, folder, "SKILL.md"), "utf8")]);
9123
+ const { copy } = await skillCopyOf(ctx.files, cli, ctx.cwd);
9124
+ const [installed, shipped] = await Promise.all([readIfAny(ctx.files, join(copy, "SKILL.md")), readRequired(ctx.files, join(ctx.packageRoot, folder, "SKILL.md"))]);
8865
9125
  const stated = installed === void 0 ? void 0 : skillVersionIn(installed);
8866
9126
  const left = stated !== void 0 && skillVersionIn(shipped) === void 0;
8867
9127
  const found = left ? `${copy} (${statedVersionNote(stated)})` : copy;
@@ -9039,7 +9299,7 @@ function registryRow(cli, check) {
9039
9299
  function doctorChecks(cli, launch, scope) {
9040
9300
  const install = installOf(cli);
9041
9301
  const check = install.check(launch);
9042
- const tools = toolRows(cli.tools ?? [], cli.commands);
9302
+ const tools = toolRows(cli.tools ?? [], cli.commands, launch.platform);
9043
9303
  return {
9044
9304
  checks: [
9045
9305
  buildRow(cli),
@@ -9055,6 +9315,8 @@ function doctorChecks(cli, launch, scope) {
9055
9315
  tools: new Set(tools.map(({ name }) => name))
9056
9316
  };
9057
9317
  }
9318
+ //#endregion
9319
+ //#region ../agent-cli/dist/built-ins/doctor.js
9058
9320
  const DEFAULT_DEADLINE_MS = 1e4;
9059
9321
  const MS_PER_SECOND = 1e3;
9060
9322
  const MIN_LABEL_WIDTH = 8;
@@ -9125,12 +9387,12 @@ async function rowOf(check, ctx, { read, runAs, tools }) {
9125
9387
  check: check.name,
9126
9388
  ...finding
9127
9389
  },
9128
- advisory: check.advisory === true
9390
+ check
9129
9391
  };
9130
9392
  }
9131
9393
  function nextOf(checked, doctorLine) {
9132
9394
  const fixes = checked.flatMap(({ row }) => row.fix === void 0 ? [] : [row.fix]);
9133
- const unfixed = checked.some(({ row, advisory }) => advisory && row.state === "problem" && row.fix === void 0);
9395
+ const unfixed = checked.some(({ row, check }) => check.advisory === true && row.state === "problem" && row.fix === void 0);
9134
9396
  return [...new Set(unfixed ? [...fixes, doctorLine] : fixes)];
9135
9397
  }
9136
9398
  function render({ rows, next }, style) {
@@ -9159,9 +9421,14 @@ function failure(cliName, rerun, problems) {
9159
9421
  });
9160
9422
  return new AggregateError(errors);
9161
9423
  }
9424
+ function checkedRows(error, problems, table) {
9425
+ const undeclared = problems.flatMap(({ row, check }) => row.code !== void 0 && mustDeclare(row.code, table) && !check.errors?.includes(row.code) ? [`the ${check.name} row failed with ${row.code}`] : []);
9426
+ if (undeclared.length === 0) return error;
9427
+ return new Error(`doctor: ${undeclared.join("; ")}, which its errors do not declare`, { cause: error });
9428
+ }
9162
9429
  /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
9163
9430
  function doctorCommand(cli, launch) {
9164
- return defineCommand$1({
9431
+ const doctor = defineCommand$1({
9165
9432
  description: "check the setup and say what fixes each problem",
9166
9433
  examples: [{
9167
9434
  command: `${cli.name} doctor`,
@@ -9190,15 +9457,20 @@ function doctorCommand(cli, launch) {
9190
9457
  ctx.deliver(report);
9191
9458
  ctx.signal.throwIfAborted();
9192
9459
  }
9193
- const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
9460
+ const problems = checked.filter(({ row, check }) => row.state === "problem" && check.advisory !== true);
9194
9461
  if (problems.length > 0) {
9195
9462
  ctx.deliver(report);
9196
- throw failure(cli.name, ctx.rerun(), problems);
9463
+ throw checkedRows(failure(cli.name, ctx.rerun(), problems.map(({ row }) => row)), problems, errorTableOf(cli));
9197
9464
  }
9198
9465
  return report;
9199
9466
  },
9200
9467
  render
9201
9468
  });
9469
+ const rowCodes = (cli.doctor ?? []).flatMap((row) => row.advisory === true ? [] : row.errors ?? []);
9470
+ return {
9471
+ ...doctor,
9472
+ errors: [.../* @__PURE__ */ new Set([DOCTOR_PROBLEMS, ...rowCodes])]
9473
+ };
9202
9474
  }
9203
9475
  //#endregion
9204
9476
  //#region ../agent-cli/dist/built-ins/commands.js
@@ -9298,19 +9570,6 @@ function specOf(commandLine, node, place) {
9298
9570
  return isCommand(node) ? commandSpec(commandLine, node, place) : group(commandLine, specsOf(commandLine, node, place));
9299
9571
  }
9300
9572
  //#endregion
9301
- //#region ../agent-cli/dist/error-table.js
9302
- /** The Error table a CLI declares, empty when it declares none. */
9303
- function errorTableOf({ errors }) {
9304
- return errors ?? {};
9305
- }
9306
- /** The `CliError` for a code of `Table`, as `cliError` and `ctx.fail` build it. */
9307
- function cliErrorOf(code, init, options) {
9308
- return new CliError({
9309
- ...init,
9310
- code
9311
- }, options);
9312
- }
9313
- //#endregion
9314
9573
  //#region ../agent-cli/dist/exit-classes.js
9315
9574
  const SIGNAL_EXIT_CODES = {
9316
9575
  SIGINT: 130,
@@ -9323,95 +9582,38 @@ const EXIT_CODES = {
9323
9582
  wait: 75,
9324
9583
  bug: 70
9325
9584
  };
9326
- const RUNTIME_EXIT_CLASSES = {
9327
- INVALID_INPUT: "caller",
9328
- UNKNOWN_OPTION: "caller",
9329
- UNKNOWN_COMMAND: "caller",
9330
- UNKNOWN_FIELD: "caller",
9331
- CONFIRMATION_REQUIRED: "caller",
9332
- CONFIRMATION_MISMATCH: "caller",
9333
- NOT_CONFIRMED: "operator",
9334
- NEEDS_OPERATOR: "operator",
9335
- OUTPUT_EXISTS: "caller",
9336
- STDIN_STALLED: "caller",
9337
- INTERRUPTED: "interrupted",
9338
- DEADLINE_EXCEEDED: "wait",
9339
- INTERNAL: "bug",
9340
- PERMISSION_DENIED: "operator",
9341
- DISK_FULL: "operator",
9342
- NOT_FOUND: "caller",
9343
- ALREADY_EXISTS: "caller",
9344
- NOT_A_FILE: "caller",
9345
- TOOL_FAILED: "operator",
9346
- TOOL_MISSING: "operator",
9347
- SECRET_MISSING: "operator",
9348
- PROJECT_INVALID: "operator",
9349
- DOCTOR_PROBLEMS: "operator",
9350
- NOT_GLOBAL_INSTALL: "operator",
9351
- NOT_ON_PATH: "operator",
9352
- REGISTRY_UNREACHABLE: "operator",
9353
- REGISTRY_AUTH: "operator",
9354
- INSTALL_FAILED: "operator",
9355
- NPM_FAILED: "operator",
9356
- NO_LATEST: "source"
9357
- };
9358
- function isRuntimeCode(code) {
9359
- return Object.hasOwn(RUNTIME_EXIT_CLASSES, code);
9360
- }
9361
9585
  /** The codes of `errors` that the Runtime already declares, which a CLI cannot redeclare. */
9362
9586
  function runtimeCodesIn(errors) {
9363
9587
  return Object.keys(errors).filter(isRuntimeCode);
9364
9588
  }
9365
- function exitClassOfOne(error, errors) {
9366
- if (!(error instanceof CliError)) return "bug";
9367
- if (error instanceof UsageError) return "caller";
9368
- if (isRuntimeCode(error.code)) return RUNTIME_EXIT_CLASSES[error.code];
9369
- return errors[error.code]?.class ?? "bug";
9370
- }
9371
- function isBug(error, errors) {
9372
- return exitClassOfOne(error, errors) === "bug";
9589
+ /** The class of `code` in the Runtime's Error table or else in the CLI's `errors`. */
9590
+ function classOfCode(code, errors) {
9591
+ return isRuntimeCode(code) ? runtimeErrors[code].class : errors[code]?.class;
9373
9592
  }
9374
- function isWait(error, errors) {
9375
- return exitClassOfOne(error, errors) === "wait";
9593
+ function declaredCodesOf(node) {
9594
+ return node.kind === "command" ? node.command.errors ?? [] : Object.values(node.commands).flatMap(declaredCodesOf);
9376
9595
  }
9377
- const ACT_FIRST = [
9378
- "caller",
9379
- "operator",
9380
- "source",
9381
- "wait",
9382
- "interrupted"
9383
- ];
9384
- function exitClassOf(error, errors) {
9385
- const classes = errorsOf(error).map((each) => exitClassOfOne(each, errors));
9386
- if (classes.includes("bug")) return "bug";
9387
- return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
9596
+ function isDeclarable(exitClass) {
9597
+ return exitClass !== void 0 && exitClass !== "bug" && exitClass !== "interrupted";
9388
9598
  }
9389
9599
  /**
9390
- * The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime's
9391
- * codes it can reach, and those of its own table.
9600
+ * The classes other than bug and interrupt that a run of `cli` can end in: those of the Runtime
9601
+ * codes any Command can meet, those of the codes its Commands under `root` declare, Built-ins
9602
+ * included, and those of its own table.
9392
9603
  */
9393
- function reachableExitClasses(cli) {
9394
- const runtime = [
9395
- "caller",
9396
- "operator",
9397
- "wait",
9398
- ...cli.commands === void 0 ? [] : installOf(cli).exitClasses
9604
+ function reachableExitClasses(cli, root) {
9605
+ const table = errorTableOf(cli);
9606
+ const classes = [
9607
+ ...anyCommandClasses,
9608
+ ...declaredCodesOf(root).map((code) => classOfCode(code, table)),
9609
+ ...Object.values(table).map((declaration) => declaration.class)
9399
9610
  ];
9400
- const own = Object.values(errorTableOf(cli)).map((declaration) => declaration.class);
9401
- return /* @__PURE__ */ new Set([...runtime, ...own]);
9611
+ return new Set(classes.filter(isDeclarable));
9402
9612
  }
9403
9613
  /** The signal named by an interrupt's abort reason; any other reason counts as Ctrl-C. */
9404
9614
  function stopSignalOf(reason) {
9405
9615
  return reason === "SIGTERM" ? "SIGTERM" : "SIGINT";
9406
9616
  }
9407
- /**
9408
- * The exit code of a run that failed with `error`, given the CLI's error table; `stop` is the
9409
- * signal that interrupted it, if any.
9410
- */
9411
- function exitCodeOf(error, errors, stop) {
9412
- const exitClass = exitClassOf(error, errors);
9413
- return exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass];
9414
- }
9415
9617
  //#endregion
9416
9618
  //#region ../agent-cli/dist/agent-cli.js
9417
9619
  function checkOwnCodes(errors) {
@@ -9443,9 +9645,11 @@ function checkCli(cli) {
9443
9645
  checkSkill(cli);
9444
9646
  const { commands } = cli;
9445
9647
  checkTree(commands, [], table);
9648
+ for (const row of cli.doctor ?? []) checkErrors(row.errors ?? [], `the ${row.name} row of doctor`, table);
9446
9649
  const taken = Object.keys(builtInCommands(cli, {
9447
9650
  bin: void 0,
9448
- node: void 0
9651
+ node: void 0,
9652
+ platform: process.platform
9449
9653
  })).filter((name) => commands[name] !== void 0);
9450
9654
  if (taken.length > 0) throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
9451
9655
  return cli;
@@ -9833,37 +10037,6 @@ function route(root, invocation) {
9833
10037
  return walk(root, argv.filter((word, index) => !(index < options.length && word === JSON_FLAG.flag)), json, invocation);
9834
10038
  }
9835
10039
  //#endregion
9836
- //#region ../agent-cli/dist/command-line/route-line.js
9837
- /** Routes `argv`, the line's words after the Run-as, to a Command of `root`. */
9838
- function routeLine(root, runAs, argv) {
9839
- if (!argv.ok) return {
9840
- ok: false,
9841
- reason: `it does not tokenize: ${argv.reason}`
9842
- };
9843
- const invocation = {
9844
- runAs,
9845
- argv: argv.words,
9846
- flags: []
9847
- };
9848
- const routed = route(root, invocation);
9849
- switch (routed.kind) {
9850
- case "usage error": return {
9851
- ok: false,
9852
- reason: routed.error.message
9853
- };
9854
- case "help":
9855
- case "version": return {
9856
- ok: false,
9857
- reason: "it names no Command"
9858
- };
9859
- case "command": return {
9860
- ok: true,
9861
- routed,
9862
- invocation
9863
- };
9864
- }
9865
- }
9866
- //#endregion
9867
10040
  //#region ../agent-cli/dist/command-line/shell-words.js
9868
10041
  const BLANK = /\s/;
9869
10042
  const SHELL_SYNTAX = /* @__PURE__ */ new Set([
@@ -9962,39 +10135,126 @@ function shellWords(line) {
9962
10135
  };
9963
10136
  }
9964
10137
  //#endregion
9965
- //#region ../agent-cli/dist/check-next-steps.js
9966
- function argvOf(words, runAs) {
9967
- const line = shellWords(lineOf(words, runAs));
9968
- const lead = shellWords(runAs);
9969
- if (!lead.ok) return lead;
9970
- if (!line.ok) return line;
9971
- return {
9972
- ok: true,
9973
- words: line.words.slice(lead.words.length)
10138
+ //#region ../agent-cli/dist/command-line/line-problem.js
10139
+ const STDIN_TEXT = "text from stdin";
10140
+ /**
10141
+ * Why `line` would not run a Command of `root`: it does not tokenize, start with its lead, route to
10142
+ * a Command or pass its schemas; `undefined` when it would.
10143
+ */
10144
+ async function lineProblem(root, { text, lead, runAs }) {
10145
+ const words = shellWords(text);
10146
+ if (!words.ok) return `it does not tokenize: ${words.reason}`;
10147
+ const leadWords = shellWords(lead);
10148
+ if (!leadWords.ok) return `its lead does not tokenize: ${leadWords.reason}`;
10149
+ if (leadWords.words.some((word, index) => words.words[index] !== word)) return `it does not start with \`${lead}\``;
10150
+ const invocation = {
10151
+ runAs,
10152
+ argv: words.words.slice(leadWords.words.length),
10153
+ flags: []
9974
10154
  };
10155
+ const routed = route(root, invocation);
10156
+ switch (routed.kind) {
10157
+ case "usage error": return routed.error.message;
10158
+ case "help":
10159
+ case "version": return "it names no Command";
10160
+ case "command": try {
10161
+ await parseCommand(routed, invocation, async () => STDIN_TEXT);
10162
+ return;
10163
+ } catch (error) {
10164
+ if (error instanceof UsageError) return error.message;
10165
+ throw error;
10166
+ }
10167
+ }
9975
10168
  }
10169
+ //#endregion
10170
+ //#region ../agent-cli/dist/check-next-steps.js
9976
10171
  /**
9977
10172
  * Why `step` would not run a Command of `root`, or `undefined` when it would. A line given whole is
9978
10173
  * not checked, since it may run another program.
9979
10174
  */
9980
- function nextStepProblem(root, runAs, step) {
10175
+ async function nextStepProblem(root, runAs, step) {
9981
10176
  const words = wordsOf(step);
9982
10177
  if (words === void 0) return void 0;
9983
- const line = routeLine(root, runAs, argvOf(words, runAs));
9984
- if (!line.ok) return line.reason;
9985
- const { routed } = line;
9986
- const { unknownOptions } = tokenize(routed.words, knownFlags(routed.spec));
9987
- return unknownOptions.length === 0 ? void 0 : `unknown option ${unknownOptions.join(", ")}`;
9988
- }
9989
- /** `error`, or a bug naming each of its Next steps that would not run. */
9990
- function checkedNextSteps(error, check, runAs) {
9991
- const problems = errorsOf(error).flatMap((leaf) => leaf instanceof CliError ? leaf.next.flatMap((step) => {
9992
- const problem = check(step);
9993
- return problem === void 0 ? [] : [`\`${lineOf(step, runAs)}\`, a Next step of ${leaf.code}: ${problem}`];
9994
- }) : []);
10178
+ return lineProblem(root, {
10179
+ text: lineOf(words, runAs),
10180
+ lead: runAs,
10181
+ runAs
10182
+ });
10183
+ }
10184
+ /** The check that a failure's Next steps run Commands of `root`: a bug names each that would not. */
10185
+ function nextStepCheck(root, runAs) {
10186
+ return (error) => checkedNextSteps(error, root, runAs);
10187
+ }
10188
+ async function checkedNextSteps(error, root, runAs) {
10189
+ const problems = [];
10190
+ for (const leaf of errorsOf(error)) {
10191
+ if (!(leaf instanceof CliError)) continue;
10192
+ for (const step of leaf.next) {
10193
+ const problem = await nextStepProblem(root, runAs, step);
10194
+ if (problem !== void 0) problems.push(`\`${lineOf(step, runAs)}\`, a Next step of ${leaf.code}: ${problem}`);
10195
+ }
10196
+ }
9995
10197
  return problems.length === 0 ? error : new Error(`a Next step would not run: ${problems.join("; ")}`, { cause: error });
9996
10198
  }
9997
10199
  //#endregion
10200
+ //#region ../agent-cli/dist/failure.js
10201
+ /**
10202
+ * What a run that threw `error` reports: the interrupt and deadline rewrite, then the undeclared
10203
+ * code check, then the Next step check, each leaf classified, and the exit code they give.
10204
+ */
10205
+ async function failureOf(error, sources) {
10206
+ const { command, control, nextStepCheck } = sources;
10207
+ const rewritten = control === void 0 ? error : control.errorAfter(error);
10208
+ const declared = command === void 0 ? rewritten : checkedCodes(rewritten, command, sources);
10209
+ return failureFrom(classified(nextStepCheck === void 0 ? declared : await nextStepCheck(declared), sources.errors), sources.stop);
10210
+ }
10211
+ /** `failure` with `error`'s leaves after its own, which may change its exit code. */
10212
+ function withLeaves(failure, error, sources) {
10213
+ return failureFrom([...failure.leaves, ...classified(error, sources.errors)], sources.stop);
10214
+ }
10215
+ function classified(error, errors) {
10216
+ return errorsOf(error).map((leaf) => ({
10217
+ error: leaf,
10218
+ exitClass: exitClassOfOne(leaf, errors)
10219
+ }));
10220
+ }
10221
+ function failureFrom(leaves, stop) {
10222
+ const exitClass = exitClassOf(leaves);
10223
+ return {
10224
+ leaves,
10225
+ exitCode: exitClass === "interrupted" ? SIGNAL_EXIT_CODES[stopSignalOf(stop?.reason)] : EXIT_CODES[exitClass]
10226
+ };
10227
+ }
10228
+ function exitClassOfOne(error, errors) {
10229
+ if (!(error instanceof CliError)) return "bug";
10230
+ if (error instanceof UsageError) return "caller";
10231
+ return classOfCode(error.code, errors) ?? "bug";
10232
+ }
10233
+ const ACT_FIRST = [
10234
+ "caller",
10235
+ "operator",
10236
+ "source",
10237
+ "wait",
10238
+ "interrupted"
10239
+ ];
10240
+ function exitClassOf(leaves) {
10241
+ const classes = leaves.map((leaf) => leaf.exitClass);
10242
+ if (classes.includes("bug")) return "bug";
10243
+ return ACT_FIRST.find((exitClass) => classes.includes(exitClass)) ?? "bug";
10244
+ }
10245
+ /**
10246
+ * `error`, or a bug naming each code in it that the Command must declare in its `errors` and does
10247
+ * not: one of the Error table, or a Runtime code only some Commands reach.
10248
+ */
10249
+ function checkedCodes(error, { command, path }, { cliName, errors }) {
10250
+ const declared = new Set(command.errors ?? []);
10251
+ const undeclared = errorsOf(error).flatMap((leaf) => leaf instanceof CliError && mustDeclare(leaf.code, errors) && !declared.has(leaf.code) ? [leaf.code] : []);
10252
+ if (undeclared.length === 0) return error;
10253
+ const label = commandLabel(path, cliName).join(" ");
10254
+ const codes = [...new Set(undeclared)].join(", ");
10255
+ return new Error(`\`${label}\` threw ${codes}, which its errors do not declare`, { cause: error });
10256
+ }
10257
+ //#endregion
9998
10258
  //#region ../agent-cli/dist/help.js
9999
10259
  function table(rows) {
10000
10260
  const width = Math.max(...rows.map(([left]) => left.length));
@@ -10032,10 +10292,10 @@ const EXIT_MEANINGS = {
10032
10292
  source: "someone at the source acts",
10033
10293
  wait: "not done yet: do what the hint says, then run it again"
10034
10294
  };
10035
- function exitCodeRows(cli) {
10295
+ function exitCodeRows(cli, root) {
10036
10296
  return [
10037
10297
  ["0", "done"],
10038
- ...[...[...reachableExitClasses(cli)].map((exitClass) => [EXIT_CODES[exitClass], EXIT_MEANINGS[exitClass]]), [EXIT_CODES.bug, `a bug in ${cli.name}`]].sort(([a], [b]) => a - b).map(([code, meaning]) => [String(code), meaning]),
10298
+ ...[...[...reachableExitClasses(cli, root)].map((exitClass) => [EXIT_CODES[exitClass], EXIT_MEANINGS[exitClass]]), [EXIT_CODES.bug, `a bug in ${cli.name}`]].sort(([a], [b]) => a - b).map(([code, meaning]) => [String(code), meaning]),
10039
10299
  [Object.values(SIGNAL_EXIT_CODES).join(", "), "interrupted"]
10040
10300
  ];
10041
10301
  }
@@ -10053,7 +10313,7 @@ function examplesSection(examples, style) {
10053
10313
  }
10054
10314
  /** The CLI's own help: a listing or its single Command's help, ending with exit codes and examples. */
10055
10315
  function cliHelp(cli, root, style) {
10056
- return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${cli.name} ${cli.version}`, root, style, cli.description), ...section("Exit codes", exitCodeRows(cli), style), ...examplesSection(rootExamples(root), style));
10316
+ return lines(...root.kind === "command" ? commandLines(root, style, { examples: false }) : listingLines(`${cli.name} ${cli.version}`, root, style, cli.description), ...section("Exit codes", exitCodeRows(cli, root), style), ...examplesSection(rootExamples(root), style));
10057
10317
  }
10058
10318
  function groupHelp(group, style) {
10059
10319
  return lines(...listingLines(group.commandLine, group, style));
@@ -10085,6 +10345,9 @@ function argRow(field) {
10085
10345
  function optionRow(field) {
10086
10346
  return fieldRow(field, defaultsOf(field));
10087
10347
  }
10348
+ function secretRow(declaration) {
10349
+ return [isNeeded(declaration) ? declaration.name : `${declaration.name} (optional)`, `get it from ${declaration.get}`];
10350
+ }
10088
10351
  function flagRow({ usage, description }) {
10089
10352
  return [usage, description];
10090
10353
  }
@@ -10095,6 +10358,7 @@ function commandLines(spec, style, { examples }) {
10095
10358
  ...examples ? examplesSection(spec.examples, style) : [],
10096
10359
  ...section("Arguments", spec.args.map(argRow), style),
10097
10360
  ...section("Options", spec.options.map(optionRow), style),
10361
+ ...section("Secrets", (spec.command.secrets ?? []).map(secretRow), style),
10098
10362
  ...spec.help === void 0 ? [] : ["", spec.help],
10099
10363
  "",
10100
10364
  `${style.bold("Effect:")} ${spec.effect} (${EFFECT_NOTES[spec.effect]})`,
@@ -10127,7 +10391,7 @@ function commandData(spec, table) {
10127
10391
  secrets: spec.command.secrets ?? [],
10128
10392
  errors: (spec.command.errors ?? []).map((code) => ({
10129
10393
  code,
10130
- class: table[code]?.class
10394
+ class: classOfCode(code, table)
10131
10395
  })),
10132
10396
  args: inputSchema(spec.command.args ?? object({})),
10133
10397
  options: optionsSchema(spec.options),
@@ -10160,7 +10424,10 @@ function errorsData(errors) {
10160
10424
  function publishedTable(table, root) {
10161
10425
  if (root.kind !== "command") return table;
10162
10426
  const declared = new Set(root.command.errors ?? []);
10163
- return Object.fromEntries(Object.entries(table).filter(([code]) => declared.has(code)));
10427
+ return Object.fromEntries(Object.entries({
10428
+ ...table,
10429
+ ...declaredRuntimeErrors
10430
+ }).filter(([code]) => declared.has(code)));
10164
10431
  }
10165
10432
  function cliHelpJson(cli, root) {
10166
10433
  const { name, version, description } = cli;
@@ -10210,15 +10477,20 @@ function noticeLine(text, json) {
10210
10477
  function stderrLine(key, text, json) {
10211
10478
  return json ? `${JSON.stringify({ [key]: text })}\n` : `${text}\n`;
10212
10479
  }
10213
- function errorText(error, format) {
10214
- const text = formatRuntimeError(error, {
10480
+ function errorText({ leaves }, format) {
10481
+ const text = formatRuntimeError(leaves, {
10215
10482
  ...format,
10216
- runAs: format.invocation.runAs,
10217
- isBug: (each) => isBug(each, format.errors),
10218
- isWait: (each) => isWait(each, format.errors)
10483
+ runAs: format.invocation.runAs
10219
10484
  });
10220
10485
  const { usageHelp, json } = format;
10221
- return usageHelp !== void 0 && !json && error instanceof UsageError ? `${text}\n${usageHelp}` : text;
10486
+ return usageHelp !== void 0 && !json && leaves.some((leaf) => leaf.error instanceof UsageError) ? `${text}\n${usageHelp}` : text;
10487
+ }
10488
+ function failedResult(failure, format) {
10489
+ return {
10490
+ stdout: "",
10491
+ stderr: errorText(failure, format),
10492
+ exitCode: failure.exitCode
10493
+ };
10222
10494
  }
10223
10495
  function stdoutOf({ value, render }, format, spill) {
10224
10496
  const text = printed(value, render, format);
@@ -10237,13 +10509,8 @@ const STOPPED_RESULT = {
10237
10509
  stderr: "",
10238
10510
  exitCode: SUCCESS_EXIT_CODE
10239
10511
  };
10240
- function errorResult(error, format) {
10241
- const checked = format.checkNextStep === void 0 ? error : checkedNextSteps(error, format.checkNextStep, format.invocation.runAs);
10242
- return {
10243
- stdout: "",
10244
- stderr: errorText(checked, format),
10245
- exitCode: exitCodeOf(checked, format.errors, format.stop)
10246
- };
10512
+ async function errorResult(error, format) {
10513
+ return failedResult(await failureOf(error, format), format);
10247
10514
  }
10248
10515
  async function commandResult(settled, format, spill) {
10249
10516
  if (settled.ok) try {
@@ -10257,185 +10524,84 @@ async function commandResult(settled, format, spill) {
10257
10524
  }
10258
10525
  const { error, delivered } = settled;
10259
10526
  if (delivered === void 0) return errorResult(error, format);
10527
+ const failure = await failureOf(error, format);
10260
10528
  let outcome;
10261
10529
  try {
10262
10530
  outcome = delivered();
10263
10531
  } catch (rejected) {
10264
- return errorResult(new AggregateError([...errorsOf(error), rejected]), format);
10532
+ return failedResult(withLeaves(failure, rejected, format), format);
10265
10533
  }
10266
- const result = errorResult(error, format);
10534
+ const result = failedResult(failure, format);
10267
10535
  try {
10268
10536
  return {
10269
10537
  ...result,
10270
10538
  stdout: await stdoutOf(outcome, format, spill)
10271
10539
  };
10272
10540
  } catch (spillError) {
10273
- const both = new AggregateError([...errorsOf(error), spillError]);
10274
10541
  return {
10275
10542
  ...result,
10276
- stderr: errorText(both, format)
10543
+ stderr: errorText(withLeaves(failure, spillError, format), format)
10277
10544
  };
10278
10545
  }
10279
- }
10280
- function textHelp(node, root, cli, style) {
10281
- if (node === root) return cliHelp(cli, node, style);
10282
- return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
10283
- }
10284
- function helpOf(node, root, cli, style, json) {
10285
- if (!json) return textHelp(node, root, cli, style);
10286
- if (node === root) return cliHelpJson(cli, node);
10287
- const table = errorTableOf(cli);
10288
- return node.kind === "command" ? commandHelpJson(node, table) : groupHelpJson(node, table);
10289
- }
10290
- function helpResult(node, root, cli, { style, json }) {
10291
- return {
10292
- stdout: helpOf(node, root, cli, style, json),
10293
- stderr: "",
10294
- exitCode: SUCCESS_EXIT_CODE
10295
- };
10296
- }
10297
- function versionResult(cli, packageRoot) {
10298
- return {
10299
- stdout: `${[
10300
- versionLine(cli),
10301
- ...buildStamp() === void 0 ? [] : [`changelog ${join(packageRoot, "CHANGELOG.md")}`],
10302
- ...cli.docs === void 0 ? [] : [`docs ${join(packageRoot, cli.docs)}`]
10303
- ].join("\n")}\n`,
10304
- stderr: "",
10305
- exitCode: SUCCESS_EXIT_CODE
10306
- };
10307
- }
10308
- //#endregion
10309
- //#region ../agent-cli/dist/package-root.js
10310
- /**
10311
- * The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
10312
- * from `cwd`; the search's start when none holds one. The bin is resolved through links first,
10313
- * since npm's global bin is a link to it.
10314
- */
10315
- function packageRootOf(bin, cwd) {
10316
- const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
10317
- for (let dir = start;; dir = dirname(dir)) {
10318
- if (existsSync(join(dir, "package.json"))) return dir;
10319
- if (dirname(dir) === dir) return start;
10320
- }
10321
- }
10322
- //#endregion
10323
- //#region ../agent-cli/dist/root-spec.js
10324
- /** The CLI's Commands as routing and help see them, the Built-in Commands included. */
10325
- function rootSpec(cli, launch) {
10326
- const names = {
10327
- name: cli.name,
10328
- runAs: runAsOf(cli)
10329
- };
10330
- return cli.commands === void 0 ? {
10331
- ...commandSpec(names.runAs, cli.command, {
10332
- path: [],
10333
- builtIn: false,
10334
- names
10335
- }),
10336
- description: cli.description
10337
- } : groupSpec(names, cli.commands, builtInCommands(cli, launch));
10338
- }
10339
- //#endregion
10340
- //#region ../agent-cli/dist/command-errors.js
10341
- /**
10342
- * `error`, or a bug naming each Error table code in it that the Command does not declare in its
10343
- * `errors`. Runtime codes are never in the table, and a built-in's failures, `doctor` rows'
10344
- * included, belong to no declaration, so both pass.
10345
- */
10346
- function checkedCodes(error, { command, path, builtIn }, cli) {
10347
- if (builtIn) return error;
10348
- const table = errorTableOf(cli);
10349
- const declared = new Set(command.errors ?? []);
10350
- const undeclared = errorsOf(error).flatMap((leaf) => leaf instanceof CliError && Object.hasOwn(table, leaf.code) && !declared.has(leaf.code) ? [leaf.code] : []);
10351
- if (undeclared.length === 0) return error;
10352
- const label = commandLabel(path, cli.name).join(" ");
10353
- const codes = [...new Set(undeclared)].join(", ");
10354
- return new Error(`\`${label}\` threw ${codes}, which its errors do not declare`, { cause: error });
10355
- }
10356
- //#endregion
10357
- //#region ../agent-cli/dist/context/operator.js
10358
- const NEEDS_OPERATOR = "NEEDS_OPERATOR";
10359
- /** Fails with `NEEDS_OPERATOR` when a Coding agent runs an Operator Command. */
10360
- function checkOperator({ operator, commandLine }, env, rerun) {
10361
- if (!operator || !isAgent(env)) return;
10362
- throw new CliError({
10363
- code: NEEDS_OPERATOR,
10364
- message: `\`${commandLine}\` must be run by a person at a terminal, not a Coding agent`,
10365
- hint: "ask the operator to type ! then the Next step",
10366
- next: [rerun]
10367
- });
10368
- }
10369
- //#endregion
10370
- //#region ../agent-cli/dist/context/outputs.js
10371
- const OUTPUT_EXISTS = "OUTPUT_EXISTS";
10372
- /** Refuses a run that would overwrite existing outputs unless the caller passed `--force`. */
10373
- async function checkOutputs({ rerun, commandLine, force, files }, outputs) {
10374
- if (outputs === void 0 || force) return;
10375
- const found = await Promise.all(outputs.map((path) => files.exists(path)));
10376
- const existing = outputs.filter((_path, index) => found[index]);
10377
- if (existing.length === 0) return;
10378
- const them = existing.length === 1 ? "it" : "them";
10379
- throw new CliError({
10380
- code: OUTPUT_EXISTS,
10381
- message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
10382
- hint: `once the user agrees to replace ${them}, run the Next step`,
10383
- next: [rerun({ add: [FORCE_FLAG.flag] })]
10384
- });
10385
- }
10386
- /** Guards the writes to declared outputs of `files`, whose paths resolve against `cwd`. */
10387
- function outputsGuard(files, cwd) {
10388
- const unwritten = /* @__PURE__ */ new Set();
10389
- let forceStep = [];
10390
- const exclusiveWrite = async (path, data, options) => {
10391
- const target = resolve(cwd, path);
10392
- unwritten.delete(target);
10393
- try {
10394
- await files.write(path, data, {
10395
- ...options,
10396
- exclusive: true
10397
- });
10398
- } catch (error) {
10399
- unwritten.add(target);
10400
- if (!(error instanceof CliError) || error.code !== "ALREADY_EXISTS") throw error;
10401
- throw new CliError({
10402
- code: ALREADY_EXISTS,
10403
- message: error.message,
10404
- hint: "once the user agrees to replace it, run the Next step",
10405
- next: [forceStep]
10406
- }, { cause: error });
10407
- }
10546
+ }
10547
+ function textHelp(node, root, cli, style) {
10548
+ if (node === root) return cliHelp(cli, node, style);
10549
+ return node.kind === "command" ? commandHelp(node, style) : groupHelp(node, style);
10550
+ }
10551
+ function helpOf(node, root, cli, style, json) {
10552
+ if (!json) return textHelp(node, root, cli, style);
10553
+ if (node === root) return cliHelpJson(cli, node);
10554
+ const table = errorTableOf(cli);
10555
+ return node.kind === "command" ? commandHelpJson(node, table) : groupHelpJson(node, table);
10556
+ }
10557
+ function helpResult(node, root, cli, { style, json }) {
10558
+ return {
10559
+ stdout: helpOf(node, root, cli, style, json),
10560
+ stderr: "",
10561
+ exitCode: SUCCESS_EXIT_CODE
10408
10562
  };
10563
+ }
10564
+ function versionResult(cli, packageRoot) {
10409
10565
  return {
10410
- files: {
10411
- ...files,
10412
- write: (path, data, options) => unwritten.has(resolve(cwd, path)) ? exclusiveWrite(path, data, options) : files.write(path, data, options)
10413
- },
10414
- guard: (outputs, step) => {
10415
- forceStep = step;
10416
- for (const path of outputs) unwritten.add(resolve(cwd, path));
10417
- }
10566
+ stdout: `${[
10567
+ versionLine(cli),
10568
+ ...buildStamp() === void 0 ? [] : [`changelog ${join(packageRoot, "CHANGELOG.md")}`],
10569
+ ...cli.docs === void 0 ? [] : [`docs ${join(packageRoot, cli.docs)}`]
10570
+ ].join("\n")}\n`,
10571
+ stderr: "",
10572
+ exitCode: SUCCESS_EXIT_CODE
10418
10573
  };
10419
10574
  }
10575
+ //#endregion
10576
+ //#region ../agent-cli/dist/package-root.js
10420
10577
  /**
10421
- * Creates each output folder and a temp file in it, so a sandbox denial fails before `apply`
10422
- * spends anything. The temp file's name is the Runtime's, so a crashed run's leftover is swept.
10578
+ * The nearest folder holding a `package.json`, searched upward from the bin or, run without one,
10579
+ * from `cwd`; the search's start when none holds one. The bin is resolved through links first,
10580
+ * since npm's global bin is a link to it.
10423
10581
  */
10424
- async function probeOutputs({ files, cwd, fileError }, outputs) {
10425
- const folders = new Set(outputs.map((path) => dirname(resolve(cwd, path))));
10426
- await Promise.all([...folders].map(async (folder) => {
10427
- const probe = tempPathOf(join(folder, "outputs-probe"));
10428
- try {
10429
- await files.mkdir(folder);
10430
- await files.write(probe, "");
10431
- await files.remove(probe);
10432
- } catch (error) {
10433
- throw fileError(error, {
10434
- path: folder,
10435
- access: "write"
10436
- });
10437
- }
10438
- }));
10582
+ function packageRootOf(bin, cwd) {
10583
+ const start = bin === void 0 ? resolve(cwd) : dirname(realpathSync(bin));
10584
+ for (let dir = start;; dir = dirname(dir)) {
10585
+ if (existsSync(join(dir, "package.json"))) return dir;
10586
+ if (dirname(dir) === dir) return start;
10587
+ }
10588
+ }
10589
+ //#endregion
10590
+ //#region ../agent-cli/dist/root-spec.js
10591
+ /** The CLI's Commands as routing and help see them, the Built-in Commands included. */
10592
+ function rootSpec(cli, launch) {
10593
+ const names = {
10594
+ name: cli.name,
10595
+ runAs: runAsOf(cli)
10596
+ };
10597
+ return cli.commands === void 0 ? {
10598
+ ...commandSpec(names.runAs, cli.command, {
10599
+ path: [],
10600
+ builtIn: false,
10601
+ names
10602
+ }),
10603
+ description: cli.description
10604
+ } : groupSpec(names, cli.commands, builtInCommands(cli, launch));
10439
10605
  }
10440
10606
  //#endregion
10441
10607
  //#region ../agent-cli/dist/context/spill.js
@@ -11018,43 +11184,6 @@ function resolveOptions(options) {
11018
11184
  };
11019
11185
  }
11020
11186
  //#endregion
11021
- //#region ../agent-cli/dist/context/confirm.js
11022
- const CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED";
11023
- const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
11024
- function checkConfirmation({ rerun, commandLine, given }, expected) {
11025
- if (given === expected) return;
11026
- const advice = {
11027
- hint: "once the user confirms the target, run the Next step",
11028
- next: [rerun({
11029
- remove: [CONFIRM_FLAG.flag],
11030
- add: [CONFIRM_FLAG.flag, expected]
11031
- })]
11032
- };
11033
- if (given === void 0) throw new UsageError({
11034
- code: CONFIRMATION_REQUIRED,
11035
- message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
11036
- ...advice
11037
- });
11038
- throw new UsageError({
11039
- code: CONFIRMATION_MISMATCH,
11040
- message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
11041
- ...advice
11042
- });
11043
- }
11044
- //#endregion
11045
- //#region ../agent-cli/dist/context/cost.js
11046
- const NOT_CONFIRMED = "NOT_CONFIRMED";
11047
- /** Refuses a run whose Plan states a cost the caller has not approved with `--yes`. */
11048
- function checkCost({ rerun, commandLine, yes }, cost) {
11049
- if (cost === void 0 || yes) return;
11050
- throw new CliError({
11051
- code: NOT_CONFIRMED,
11052
- message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
11053
- hint: "once the user approves the cost, run the Next step",
11054
- next: [rerun({ add: [YES_FLAG.flag] })]
11055
- });
11056
- }
11057
- //#endregion
11058
11187
  //#region ../agent-cli/dist/execute.js
11059
11188
  const PLAN_INDENT = " ";
11060
11189
  function outcomeOf(value, render) {
@@ -11102,8 +11231,8 @@ function serveContextOf(ctx, end) {
11102
11231
  const serveCtx = Object.create(ctx);
11103
11232
  return Object.assign(serveCtx, { end });
11104
11233
  }
11105
- async function execute(command, input, ctx, { dryRun, confirm, yes, page, commandLine, started, files, guardOutputs }) {
11106
- await checkTools(command.tools ?? [], ctx.env, ctx.rerun());
11234
+ async function execute(command, input, ctx, { flags, commandLine, started, outputs: runOutputs }) {
11235
+ await checkTools(command.tools ?? [], ctx, ctx.rerun());
11107
11236
  const { env, envFile, fileError } = ctx;
11108
11237
  checkSecrets(command.secrets ?? [], {
11109
11238
  env,
@@ -11114,7 +11243,7 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, comman
11114
11243
  const { render } = command;
11115
11244
  return outcomeOf(await command.run({
11116
11245
  ...input,
11117
- ...page
11246
+ ...flags.page
11118
11247
  }, ctx), (data, style) => render === void 0 ? encode(data.items) : render(data, style));
11119
11248
  }
11120
11249
  if (command.effect === "read" || command.effect === "act") return dataOutcome(command, await command.run(input, ctx), commandLine);
@@ -11129,32 +11258,28 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, comman
11129
11258
  return outcome;
11130
11259
  }
11131
11260
  const plan = await command.plan(input, ctx);
11132
- if (dryRun) return dryRunOutcome(command, plan, commandLine);
11261
+ if (ctx.dryRun) return dryRunOutcome(command, plan, commandLine);
11133
11262
  const outputs = command.outputs?.(plan);
11134
- await checkOutputs({
11135
- rerun: ctx.rerun,
11263
+ const { rerun, force } = ctx;
11264
+ if (outputs !== void 0) await runOutputs.refuseOverwrite(outputs, {
11265
+ rerun,
11136
11266
  commandLine,
11137
- force: ctx.force,
11138
- files: ctx.files
11139
- }, outputs);
11267
+ force
11268
+ });
11140
11269
  checkCost({
11141
- rerun: ctx.rerun,
11270
+ rerun,
11142
11271
  commandLine,
11143
- yes
11272
+ yes: flags.yes
11144
11273
  }, command.cost?.(plan));
11145
11274
  if (command.effect === "destructive") checkConfirmation({
11146
- rerun: ctx.rerun,
11275
+ rerun,
11147
11276
  commandLine,
11148
- given: confirm
11277
+ given: flags.confirm
11149
11278
  }, command.confirm(plan));
11150
- if (outputs !== void 0) {
11151
- await probeOutputs({
11152
- files,
11153
- cwd: ctx.cwd,
11154
- fileError: ctx.fileError
11155
- }, outputs);
11156
- if (!ctx.force) guardOutputs(outputs, ctx.rerun({ add: [FORCE_FLAG.flag] }));
11157
- }
11279
+ if (outputs !== void 0) await runOutputs.claim(outputs, {
11280
+ rerun,
11281
+ force
11282
+ });
11158
11283
  return dataOutcome(command, await command.apply(plan, ctx), commandLine);
11159
11284
  }
11160
11285
  const manifestSchema = object({ bugs: union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0) });
@@ -11170,16 +11295,7 @@ function manifestAt(packageRoot) {
11170
11295
  return parsed.success ? parsed.data : NO_MANIFEST;
11171
11296
  }
11172
11297
  //#endregion
11173
- //#region ../agent-cli/dist/run-command.js
11174
- function stdinNeverEndedHint(callerLine) {
11175
- return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
11176
- }
11177
- function runDeadlineAdvice(resumed, pipedLine) {
11178
- return resumed === void 0 ? { hint: stdinNeverEndedHint(pipedLine) } : {
11179
- hint: "run the Next step to continue the work",
11180
- next: [resumed]
11181
- };
11182
- }
11298
+ //#region ../agent-cli/dist/run-scope.js
11183
11299
  function selfOf(name, bin) {
11184
11300
  return bin === void 0 ? [name] : [
11185
11301
  process.execPath,
@@ -11198,57 +11314,93 @@ function portsOf(cli, replacement) {
11198
11314
  if (replacement !== void 0) return () => replacement;
11199
11315
  return cli.ports ?? (() => ({}));
11200
11316
  }
11201
- function portsBaseOf(scope, signal) {
11202
- const { cli, options, packageRoot, progress, fileError, rerun, clock, files } = scope;
11203
- const { cwd, env } = options;
11317
+ function runScopeOf(cli, routed, settings, write, baseInvocation) {
11318
+ const { cwd, env } = settings;
11319
+ const clock = settings.clock ?? realClock;
11320
+ const platform = settings.platform ?? process.platform;
11321
+ const invocation = {
11322
+ ...baseInvocation,
11323
+ flags: knownFlags(routed.spec),
11324
+ stdinArg: stdinArgOf(routed)
11325
+ };
11326
+ const rerun = (change) => ({
11327
+ kind: "rerun",
11328
+ words: changedArgv(invocation, change),
11329
+ stdinArg: invocation.stdinArg
11330
+ });
11331
+ const fileError = fileErrorOf({
11332
+ rerun: rerun(),
11333
+ env,
11334
+ platform
11335
+ });
11336
+ const outputs = outputsOf(settings.files ?? realFiles(), {
11337
+ cwd,
11338
+ fileError
11339
+ });
11340
+ const packageRoot = packageRootOf(settings.bin, cwd);
11341
+ const progress = (message) => write.stderr(progressLine(message, routed.json));
11204
11342
  const envFile = envFileOf(cli.name, env);
11343
+ const spawning = {
11344
+ fileError,
11345
+ rerun: rerun()
11346
+ };
11205
11347
  return {
11206
- cwd,
11207
- env,
11208
- packageRoot,
11209
- progress,
11210
- secret: (name) => secretOf({
11211
- envFile,
11212
- env,
11213
- fileError
11214
- }, name),
11215
- envFile,
11348
+ invocation,
11349
+ rerun,
11216
11350
  fileError,
11217
- version: cli.version,
11218
- build: buildStamp(),
11219
- self: selfOf(cli.name, options.bin),
11220
- signal,
11221
11351
  clock,
11222
- files,
11223
- spawn: contextSpawn(options.spawn ?? realSpawn, {
11224
- cwd,
11225
- env,
11226
- signal
11227
- }, {
11228
- fileError,
11229
- rerun
11230
- }),
11231
- spawnDetached: contextSpawnDetached(spawnDetachedOf(options), {
11352
+ platform,
11353
+ outputs,
11354
+ progress,
11355
+ notice: (text) => write.stderr(noticeLine(text, routed.json)),
11356
+ packageRoot,
11357
+ ports: portsOf(cli, settings.ports),
11358
+ portsBase: (signal) => ({
11232
11359
  cwd,
11233
11360
  env,
11234
- signal
11235
- }, {
11361
+ platform,
11362
+ packageRoot,
11363
+ progress,
11364
+ secret: (name) => secretOf({
11365
+ envFile,
11366
+ env,
11367
+ fileError
11368
+ }, name),
11369
+ envFile,
11236
11370
  fileError,
11237
- rerun
11371
+ version: cli.version,
11372
+ build: buildStamp(),
11373
+ self: selfOf(cli.name, settings.bin),
11374
+ signal,
11375
+ clock,
11376
+ files: outputs.files,
11377
+ spawn: contextSpawn(settings.spawn ?? realSpawn, {
11378
+ cwd,
11379
+ env,
11380
+ signal
11381
+ }, spawning),
11382
+ spawnDetached: contextSpawnDetached(spawnDetachedOf(settings), {
11383
+ cwd,
11384
+ env,
11385
+ signal
11386
+ }, spawning)
11238
11387
  })
11239
11388
  };
11240
11389
  }
11241
- function contextOf(portsBase, control, parts) {
11242
- const { ports, project, force, dryRun, secrets = [], rerun, deliver, notice } = parts;
11243
- const declared = new Set(secrets.map(({ name }) => name));
11390
+ function contextOf(scope, control, { command, flags, project, deliver }) {
11391
+ const portsBase = scope.portsBase(control.signal);
11392
+ const ports = once(() => scope.ports(portsBase));
11393
+ const declared = new Map((command.secrets ?? []).map((declaration) => [declaration.name, declaration]));
11394
+ function secret(name) {
11395
+ const declaration = declared.get(name);
11396
+ if (declaration === void 0) throw new Error(`ctx.secret("${name}") names a Secret the Command does not declare`);
11397
+ const found = portsBase.secret(name);
11398
+ if (found === void 0 && isNeeded(declaration)) throw new Error(`ctx.secret("${name}") found no value`);
11399
+ return found;
11400
+ }
11244
11401
  return {
11245
11402
  ...portsBase,
11246
- secret: (name) => {
11247
- if (!declared.has(name)) throw new Error(`ctx.secret("${name}") names a Secret the Command does not declare`);
11248
- const secret = portsBase.secret(name);
11249
- if (secret === void 0) throw new Error(`ctx.secret("${name}") found no value`);
11250
- return secret;
11251
- },
11403
+ secret,
11252
11404
  fail: cliErrorOf,
11253
11405
  deadline: (ms, options) => control.deadline(ms, options),
11254
11406
  runToEnd: (name, work) => control.runToEnd(name, work),
@@ -11256,47 +11408,57 @@ function contextOf(portsBase, control, parts) {
11256
11408
  return ports();
11257
11409
  },
11258
11410
  project,
11259
- force,
11260
- dryRun,
11261
- rerun,
11411
+ force: flags.force,
11412
+ dryRun: flags.dryRun,
11413
+ rerun: scope.rerun,
11262
11414
  poll: (check, { intervalMs, signal: stop }) => poll(portsBase.clock, check, intervalMs, stop === void 0 ? control.signal : AbortSignal.any([control.signal, stop])),
11263
11415
  deliver,
11264
- notice
11416
+ notice: scope.notice
11417
+ };
11418
+ }
11419
+ /** `build` runs on the first call; later calls give its value or throw its error again. */
11420
+ function once(build) {
11421
+ let outcome;
11422
+ return () => {
11423
+ if (outcome === void 0) try {
11424
+ outcome = {
11425
+ ok: true,
11426
+ value: build()
11427
+ };
11428
+ } catch (error) {
11429
+ outcome = {
11430
+ ok: false,
11431
+ error
11432
+ };
11433
+ }
11434
+ if (outcome.ok) return outcome.value;
11435
+ throw outcome.error;
11436
+ };
11437
+ }
11438
+ //#endregion
11439
+ //#region ../agent-cli/dist/run-command.js
11440
+ function stdinNeverEndedHint(callerLine) {
11441
+ return `stdin never ended: pipe the text into \`${callerLine}\`, or redirect a file into it`;
11442
+ }
11443
+ function runDeadlineAdvice(resumed, pipedLine) {
11444
+ return resumed === void 0 ? { hint: stdinNeverEndedHint(pipedLine) } : {
11445
+ hint: "run the Next step to continue the work",
11446
+ next: [resumed]
11265
11447
  };
11266
11448
  }
11267
- /** Leaves the Side task running, so the caller writes the result before it waits for it. */
11449
+ /**
11450
+ * Leaves the Side task running, so the caller writes the result before it waits for it.
11451
+ *
11452
+ * A gate that needs no `ctx` (operator, parse, Project file) runs here; one that does (tools,
11453
+ * Secrets, the Plan's) runs in `execute`, after the Notices, so a failing run still delivers them.
11454
+ */
11268
11455
  async function runCommand(cli, routed, options, write, baseFormat) {
11269
11456
  const { command, commandLine, builtIn } = routed.spec;
11270
11457
  const { cwd, env } = options;
11271
- const clock = options.clock ?? realClock;
11272
- const files = options.files ?? realFiles();
11273
11458
  const stdin = options.stdin ?? "";
11274
- const platform = options.platform ?? process.platform;
11275
- const ports = portsOf(cli, options.ports);
11276
- const packageRoot = packageRootOf(options.bin, cwd);
11277
- const progress = (message) => write.stderr(progressLine(message, routed.json));
11278
- const invocation = {
11279
- ...baseFormat.invocation,
11280
- flags: knownFlags(routed.spec),
11281
- stdinArg: stdinArgOf(routed)
11282
- };
11283
- const rerunOf = (change) => ({
11284
- kind: "rerun",
11285
- words: changedArgv(invocation, change),
11286
- stdinArg: invocation.stdinArg
11287
- });
11459
+ const scope = runScopeOf(cli, routed, options, write, baseFormat.invocation);
11460
+ const { invocation, rerun: rerunOf, fileError, clock, outputs, progress, platform } = scope;
11288
11461
  const rerun = rerunOf();
11289
- const fileError = fileErrorOf({
11290
- rerun,
11291
- env,
11292
- platform
11293
- });
11294
- const format = {
11295
- ...baseFormat,
11296
- bugs: () => manifestAt(packageRoot).bugs,
11297
- json: routed.json,
11298
- invocation
11299
- };
11300
11462
  const pipedLine = commandLineOf({
11301
11463
  ...invocation,
11302
11464
  stdinArg: void 0
@@ -11313,10 +11475,18 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11313
11475
  advice: () => runDeadlineAdvice(resumed, pipedLine)
11314
11476
  }
11315
11477
  });
11316
- const failed = (error, finishSideTask) => {
11478
+ const format = {
11479
+ ...baseFormat,
11480
+ command: routed.spec,
11481
+ control,
11482
+ bugs: () => manifestAt(scope.packageRoot).bugs,
11483
+ json: routed.json,
11484
+ invocation
11485
+ };
11486
+ const failed = async (error, finishSideTask) => {
11317
11487
  control.close();
11318
11488
  return {
11319
- result: errorResult(error, format),
11489
+ result: await errorResult(error, format),
11320
11490
  finishSideTask
11321
11491
  };
11322
11492
  };
@@ -11335,24 +11505,9 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11335
11505
  }
11336
11506
  const { input, flags } = parsed;
11337
11507
  resumed = command.deadline?.resume(input, rerunOf);
11338
- const outputs = outputsGuard(classifiedFiles(files, {
11339
- cwd,
11340
- fileError
11341
- }), cwd);
11342
- const scope = {
11343
- cli,
11344
- options,
11345
- packageRoot,
11346
- progress,
11347
- fileError,
11348
- rerun,
11349
- clock,
11350
- files: outputs.files
11351
- };
11352
- const portsBase = portsBaseOf(scope, control.signal);
11353
11508
  const finishSideTask = startSideTask(builtIn ? void 0 : cli.sideTask?.bind(cli), (sideSignal) => {
11354
- const sideBase = portsBaseOf(scope, sideSignal);
11355
- const sidePorts = once(() => ports(sideBase));
11509
+ const sideBase = scope.portsBase(sideSignal);
11510
+ const sidePorts = once(() => scope.ports(sideBase));
11356
11511
  return {
11357
11512
  ...sideBase,
11358
11513
  command: routed.spec.path,
@@ -11377,14 +11532,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11377
11532
  return failed(error, finishSideTask);
11378
11533
  }
11379
11534
  let delivered;
11380
- const ctx = contextOf(portsBase, control, {
11381
- ports: once(() => ports(portsBase)),
11535
+ const ctx = contextOf(scope, control, {
11536
+ command,
11537
+ flags,
11382
11538
  project,
11383
- force: flags.force,
11384
- dryRun: flags.dryRun,
11385
- secrets: command.secrets,
11386
- rerun: rerunOf,
11387
- notice: (text) => write.stderr(noticeLine(text, routed.json)),
11388
11539
  deliver: (data) => {
11389
11540
  delivered = () => dataOutcome(command, data, commandLine);
11390
11541
  }
@@ -11403,19 +11554,16 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11403
11554
  const outcome = await control.settle(noticesOf(cli, {
11404
11555
  builtIn,
11405
11556
  bin: options.bin,
11406
- node: options.node
11557
+ node: options.node,
11558
+ platform
11407
11559
  }, ctx).then(() => execute(command, input, ctx, {
11408
- dryRun: flags.dryRun,
11409
- confirm: flags.confirm,
11410
- yes: flags.yes,
11411
- page: flags.page,
11560
+ flags,
11412
11561
  commandLine,
11413
11562
  started: async (outcome) => {
11414
11563
  write.stdout(await startedOutput(outcome, format, spill));
11415
11564
  served = true;
11416
11565
  },
11417
- files,
11418
- guardOutputs: outputs.guard
11566
+ outputs
11419
11567
  })));
11420
11568
  if (served) return {
11421
11569
  result: STOPPED_RESULT,
@@ -11432,7 +11580,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11432
11580
  };
11433
11581
  settled = {
11434
11582
  ok: false,
11435
- error: checkedCodes(control.errorAfter(error), routed.spec, cli),
11583
+ error,
11436
11584
  delivered
11437
11585
  };
11438
11586
  }
@@ -11444,25 +11592,6 @@ async function runCommand(cli, routed, options, write, baseFormat) {
11444
11592
  finishSideTask
11445
11593
  };
11446
11594
  }
11447
- /** `build` runs on the first call; later calls give its value or throw its error again. */
11448
- function once(build) {
11449
- let outcome;
11450
- return () => {
11451
- if (outcome === void 0) try {
11452
- outcome = {
11453
- ok: true,
11454
- value: build()
11455
- };
11456
- } catch (error) {
11457
- outcome = {
11458
- ok: false,
11459
- error
11460
- };
11461
- }
11462
- if (outcome.ok) return outcome.value;
11463
- throw outcome.error;
11464
- };
11465
- }
11466
11595
  async function noticesOf(cli, launch, ctx) {
11467
11596
  await installOf(cli).notice(launch, ctx);
11468
11597
  }
@@ -11521,7 +11650,8 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
11521
11650
  const runAs = runAsOf(cli);
11522
11651
  const root = rootSpec(cli, {
11523
11652
  bin: options.bin,
11524
- node: options.node
11653
+ node: options.node,
11654
+ platform: options.platform ?? process.platform
11525
11655
  });
11526
11656
  const format = {
11527
11657
  cliName: cli.name,
@@ -11535,7 +11665,7 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
11535
11665
  json: false,
11536
11666
  errors: errorTableOf(cli),
11537
11667
  stop: options.signal,
11538
- ...options.checkNextSteps === true ? { checkNextStep: (step) => nextStepProblem(root, runAs, step) } : {}
11668
+ ...options.checkNextSteps === true ? { nextStepCheck: nextStepCheck(root, runAs) } : {}
11539
11669
  };
11540
11670
  const routed = route(root, format.invocation);
11541
11671
  const misused = (node) => isAgent(options.env) ? {
@@ -11614,7 +11744,7 @@ async function runProcess(cli) {
11614
11744
  //#region package.json
11615
11745
  var package_default = {
11616
11746
  name: "@duyquangnvx/create-agent-cli",
11617
- version: "0.26.0",
11747
+ version: "0.28.0",
11618
11748
  description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
11619
11749
  type: "module",
11620
11750
  engines: { "node": ">=22.12.0" },