@duyquangnvx/create-agent-cli 0.15.0 → 0.15.1

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/bin.js +738 -702
  3. package/package.json +2 -2
package/dist/bin.js CHANGED
@@ -6496,15 +6496,6 @@ function isCommand(node) {
6496
6496
  return typeof node.effect === "string";
6497
6497
  }
6498
6498
  //#endregion
6499
- //#region ../agent-cli/dist/run-as.js
6500
- /**
6501
- * The Run-as: how every command line the Runtime gives its caller starts. A Dependency install runs
6502
- * the copy the project pins, so it is `npx -q --no-install <bin>`; otherwise the bare name (ADR 0011).
6503
- */
6504
- function runAsOf(cli) {
6505
- return cli.install?.mode === "dependency" ? `npx -q --no-install ${cli.name}` : cli.name;
6506
- }
6507
- //#endregion
6508
6499
  //#region ../agent-cli/dist/project.js
6509
6500
  const PROJECT_INVALID = "PROJECT_INVALID";
6510
6501
  function invalid(path, problem, callerLine, options) {
@@ -6619,6 +6610,69 @@ function versionLine({ version, build }) {
6619
6610
  return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
6620
6611
  }
6621
6612
  //#endregion
6613
+ //#region ../agent-cli/dist/project-root.js
6614
+ const MANAGERS = [
6615
+ "pnpm",
6616
+ "npm",
6617
+ "yarn",
6618
+ "bun"
6619
+ ];
6620
+ const LOCKFILES = [
6621
+ ["pnpm-lock.yaml", "pnpm"],
6622
+ ["package-lock.json", "npm"],
6623
+ ["yarn.lock", "yarn"],
6624
+ ["bun.lock", "bun"],
6625
+ ["bun.lockb", "bun"]
6626
+ ];
6627
+ const manifestSchema$1 = object({
6628
+ packageManager: string().optional(),
6629
+ dependencies: record(string(), unknown()).optional(),
6630
+ devDependencies: record(string(), unknown()).optional()
6631
+ });
6632
+ /** `dir` and every folder above it, nearest first. */
6633
+ function ancestorsOf(dir) {
6634
+ const parent = dirname(dir);
6635
+ return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
6636
+ }
6637
+ async function exists(path) {
6638
+ return await orMissing(stat(path)) !== void 0;
6639
+ }
6640
+ /** Whether `path` lies strictly inside `dir`. */
6641
+ function isInside(dir, path) {
6642
+ const inner = relative(dir, path);
6643
+ return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
6644
+ }
6645
+ async function manifestIn(dir) {
6646
+ let text;
6647
+ try {
6648
+ text = await readFile(join(dir, "package.json"), "utf8");
6649
+ } catch (error) {
6650
+ if (isMissingFile(error)) return void 0;
6651
+ throw error;
6652
+ }
6653
+ let manifest;
6654
+ try {
6655
+ manifest = JSON.parse(text);
6656
+ } catch {
6657
+ return;
6658
+ }
6659
+ return manifestSchema$1.safeParse(manifest).data;
6660
+ }
6661
+ async function declaredManager(dir) {
6662
+ const name = (await manifestIn(dir))?.packageManager?.split("@")[0];
6663
+ return MANAGERS.find((manager) => manager === name);
6664
+ }
6665
+ async function lockfileRootOf(cwd) {
6666
+ for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await exists(join(dir, file))) return {
6667
+ dir,
6668
+ manager: await declaredManager(dir) ?? kind
6669
+ };
6670
+ }
6671
+ /** The package manager's project root: the folder of the nearest lockfile, or `cwd` without one. */
6672
+ async function projectRootOf(cwd) {
6673
+ return (await lockfileRootOf(cwd))?.dir ?? cwd;
6674
+ }
6675
+ //#endregion
6622
6676
  //#region ../agent-cli/dist/tail.js
6623
6677
  /** The last `count` lines of a child's output, trimmed; empty when it wrote nothing. */
6624
6678
  function lastLines(output, count) {
@@ -7003,7 +7057,6 @@ function npmContext(cli, registry, ctx, node, cwd) {
7003
7057
  node
7004
7058
  };
7005
7059
  }
7006
- const NPMJS = "https://registry.npmjs.org/";
7007
7060
  const STDERR_TAIL_LINES = 10;
7008
7061
  const RM_RETRIES = 3;
7009
7062
  const NETWORK_CODES = /* @__PURE__ */ new Set([
@@ -7022,28 +7075,6 @@ const NPM_CODE = /^npm (?:error|ERR!) code (\S+)$/m;
7022
7075
  function scopeOf(name) {
7023
7076
  return /^(@[^/]+)\//.exec(name)?.[1];
7024
7077
  }
7025
- /** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
7026
- function registryFlags({ package: name, registry }) {
7027
- if (registry === NPMJS) return [];
7028
- const scope = scopeOf(name);
7029
- return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
7030
- }
7031
- /** The registry a Global install declares. */
7032
- function globalRegistry(install) {
7033
- const { package: name, registry } = install;
7034
- return {
7035
- package: name,
7036
- label: registry,
7037
- flags: registryFlags(install),
7038
- unreachableHint: "check the network or VPN",
7039
- loginHint: `log in to ${registry} (\`npm login --registry=${registry}\`) or set its token in ~/.npmrc`,
7040
- missingHint: `check that ${name} is published on ${registry}`
7041
- };
7042
- }
7043
- /** npm for a Global install, run where the CLI was started. */
7044
- function globalNpm(cli, install, ctx, node) {
7045
- return npmContext(cli, globalRegistry(install), ctx, node, process.cwd());
7046
- }
7047
7078
  /** The command a person runs to install the CLI globally from its registry. */
7048
7079
  function installCommandLine(registry, version = "latest") {
7049
7080
  return [
@@ -7186,62 +7217,13 @@ async function latestVersion(ctx) {
7186
7217
  return version;
7187
7218
  }
7188
7219
  //#endregion
7189
- //#region ../agent-cli/dist/built-ins/global-install.js
7190
- const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
7191
- /** Where npm puts global packages under its global prefix. */
7192
- function globalModulesDir(prefix) {
7193
- return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
7194
- }
7195
- function notGlobal(ctx, message) {
7196
- return new CliError({
7197
- code: NOT_GLOBAL_INSTALL,
7198
- message,
7199
- hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.registry)}`
7200
- });
7201
- }
7202
- async function realpathOrUndefined(path) {
7203
- return realpath(path).catch((error) => {
7204
- if (isMissingFile(error)) return void 0;
7205
- throw error;
7206
- });
7207
- }
7208
- /** Whether `path` lies strictly inside `dir`. */
7209
- function isInside(dir, path) {
7210
- const inner = relative(dir, path);
7211
- return inner !== "" && !inner.startsWith("..") && !isAbsolute(inner);
7212
- }
7213
- /**
7214
- * The global npm install this copy runs from, found by asking npm for its global prefix.
7215
- * `bin` is the script that started the CLI; throws `NOT_GLOBAL_INSTALL` when it is not inside that install.
7216
- */
7217
- async function globalInstallOf(bin, ctx) {
7218
- if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
7219
- const prefix = await globalPrefix(ctx);
7220
- const packageDir = join(globalModulesDir(prefix), ctx.registry.package);
7221
- const installed = await realpathOrUndefined(packageDir);
7222
- const here = await realpathOrUndefined(bin) ?? bin;
7223
- if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
7224
- if ((await lstat(packageDir)).isSymbolicLink()) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
7225
- return {
7226
- prefix,
7227
- packageDir
7228
- };
7220
+ //#region ../agent-cli/dist/built-ins/install/run-as.js
7221
+ /** The Run-as: the bare name, or `npx -q --no-install <bin>` for a Dependency install (ADR 0011). */
7222
+ function runAsOf({ name, install }) {
7223
+ return install?.mode === "dependency" ? `npx -q --no-install ${name}` : name;
7229
7224
  }
7230
7225
  //#endregion
7231
- //#region ../agent-cli/dist/built-ins/dependency-install.js
7232
- const MANAGERS = [
7233
- "pnpm",
7234
- "npm",
7235
- "yarn",
7236
- "bun"
7237
- ];
7238
- const LOCKFILES = [
7239
- ["pnpm-lock.yaml", "pnpm"],
7240
- ["package-lock.json", "npm"],
7241
- ["yarn.lock", "yarn"],
7242
- ["bun.lock", "bun"],
7243
- ["bun.lockb", "bun"]
7244
- ];
7226
+ //#region ../agent-cli/dist/built-ins/install/dependency.js
7245
7227
  const LINES = {
7246
7228
  pnpm: {
7247
7229
  upgrade: (packages) => `pnpm up --latest ${packages.join(" ")}`,
@@ -7254,46 +7236,7 @@ const LINES = {
7254
7236
  install: "npm install"
7255
7237
  }
7256
7238
  };
7257
- const manifestSchema$1 = object({
7258
- packageManager: string().optional(),
7259
- dependencies: record(string(), unknown()).optional(),
7260
- devDependencies: record(string(), unknown()).optional()
7261
- });
7262
7239
  const packageSchema = object({ version: string() });
7263
- /** `dir` and every folder above it, nearest first. */
7264
- function ancestorsOf(dir) {
7265
- const parent = dirname(dir);
7266
- return parent === dir ? [dir] : [dir, ...ancestorsOf(parent)];
7267
- }
7268
- async function exists(path) {
7269
- try {
7270
- await stat(path);
7271
- return true;
7272
- } catch (error) {
7273
- if (isMissingFile(error)) return false;
7274
- throw error;
7275
- }
7276
- }
7277
- async function manifestIn(dir) {
7278
- let text;
7279
- try {
7280
- text = await readFile(join(dir, "package.json"), "utf8");
7281
- } catch (error) {
7282
- if (isMissingFile(error)) return void 0;
7283
- throw error;
7284
- }
7285
- let manifest;
7286
- try {
7287
- manifest = JSON.parse(text);
7288
- } catch {
7289
- return;
7290
- }
7291
- return manifestSchema$1.safeParse(manifest).data;
7292
- }
7293
- async function declaredManager(dir) {
7294
- const name = (await manifestIn(dir))?.packageManager?.split("@")[0];
7295
- return MANAGERS.find((manager) => manager === name);
7296
- }
7297
7240
  async function manifestListing(name, cwd, root) {
7298
7241
  const dirs = ancestorsOf(cwd);
7299
7242
  const top = root === void 0 ? dirs.length : dirs.indexOf(root.dir) + 1;
@@ -7302,16 +7245,6 @@ async function manifestListing(name, cwd, root) {
7302
7245
  if (manifest?.dependencies?.[name] !== void 0 || manifest?.devDependencies?.[name] !== void 0) return join(dir, "package.json");
7303
7246
  }
7304
7247
  }
7305
- async function lockfileRootOf(cwd) {
7306
- for (const dir of ancestorsOf(cwd)) for (const [file, kind] of LOCKFILES) if (await exists(join(dir, file))) return {
7307
- dir,
7308
- manager: await declaredManager(dir) ?? kind
7309
- };
7310
- }
7311
- /** The package manager's project root: the folder of the nearest lockfile, or `cwd` without one. */
7312
- async function projectRootOf(cwd) {
7313
- return (await lockfileRootOf(cwd))?.dir ?? cwd;
7314
- }
7315
7248
  async function versionIn(manifest) {
7316
7249
  try {
7317
7250
  return packageSchema.parse(JSON.parse(await readFile(manifest, "utf8"))).version;
@@ -7341,10 +7274,7 @@ async function standingOf(install, bin, cwd, root) {
7341
7274
  kind: "linked",
7342
7275
  copy
7343
7276
  };
7344
- const runningFrom = await realpath(bin).catch((error) => {
7345
- if (isMissingFile(error)) return bin;
7346
- throw error;
7347
- });
7277
+ const runningFrom = await orMissing(realpath(bin)) ?? bin;
7348
7278
  return isInside(copy.realDir, runningFrom) ? {
7349
7279
  kind: "installed",
7350
7280
  copy
@@ -7395,7 +7325,7 @@ function problem(message, fix) {
7395
7325
  };
7396
7326
  }
7397
7327
  /** The `install` row of a Dependency install. */
7398
- function dependencyInstallFinding(cli, install, { cwd, root, standing }) {
7328
+ function installFinding(cli, runAs, install, { cwd, root, standing }) {
7399
7329
  const lines = root === void 0 ? void 0 : LINES[root.manager];
7400
7330
  switch (standing.kind) {
7401
7331
  case "source":
@@ -7405,7 +7335,7 @@ function dependencyInstallFinding(cli, install, { cwd, root, standing }) {
7405
7335
  case "unreadable": return problem(`${standing.manifest} is not a package.json with a version: ${standing.reason}`, lines?.install);
7406
7336
  case "other": {
7407
7337
  const { copy, runningFrom } = standing;
7408
- return problem(`${cli.name} runs from ${runningFrom}, not from this project's ${install.package} ${copy.version} at ${copy.realDir}`, `${runAsOf(cli)} doctor`);
7338
+ return problem(`${cli.name} runs from ${runningFrom}, not from this project's ${install.package} ${copy.version} at ${copy.realDir}`, `${runAs} doctor`);
7409
7339
  }
7410
7340
  case "installed": {
7411
7341
  const { copy } = standing;
@@ -7435,97 +7365,37 @@ function adviceFor(install, root, latest) {
7435
7365
  if (lines !== void 0) return { fix: lines.upgrade(packages) };
7436
7366
  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"}` };
7437
7367
  }
7438
- function dependencyRelease(cli, install, { cwd, root, standing }) {
7439
- if (isUnchecked(standing)) return { skipped: skippedFor(cli, install, standing) };
7440
- return {
7441
- registry: registryOf(install, root?.dir),
7442
- cwd: root?.dir ?? cwd,
7443
- advise: (latest) => adviceFor(install, root, latest)
7444
- };
7445
- }
7446
- //#endregion
7447
- //#region ../agent-cli/dist/built-ins/agents-md.js
7448
- /** One paragraph telling every session that the CLI exists, how to run it and which skill to load. */
7449
- function agentsBlock(cli) {
7450
- const { install } = cli;
7451
- const where = install?.mode === "dependency" ? `, and only from a package that has \`${install.package}\` in its dependencies; from anywhere else npx reports the package missing` : "";
7452
- const what = cli.description.replace(/\.$/, "");
7453
- return `\`${cli.name}\`: ${what}. Run it as \`${runAsOf(cli)}\`${where}. Load the \`${cli.name}\` skill before using it.`;
7454
- }
7455
- function markersOf(name) {
7456
- return {
7457
- begin: `<!-- BEGIN:${name} -->`,
7458
- end: `<!-- END:${name} -->`
7459
- };
7460
- }
7461
- /** `text` with the CLI's block between its markers, appended when they are missing; nothing else changes. */
7462
- function withBlock(cli, text) {
7463
- const { begin, end } = markersOf(cli.name);
7464
- const whole = `${begin}\n${agentsBlock(cli)}\n${end}`;
7465
- const start = text.indexOf(begin);
7466
- const stop = start === -1 ? -1 : text.indexOf(end, start);
7467
- if (stop !== -1) return `${text.slice(0, start)}${whole}${text.slice(stop + end.length)}`;
7468
- if (text === "") return `${whole}\n`;
7469
- return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
7470
- }
7471
- async function agentsMdAt(cli, root) {
7472
- const path = join(root, "AGENTS.md");
7473
- const existing = await orMissing(readFile(path, "utf8"));
7474
- const block = agentsBlock(cli);
7475
- if (existing === void 0) return {
7476
- path,
7477
- block,
7478
- change: "create"
7479
- };
7480
- if (withBlock(cli, existing) === existing) return {
7481
- path,
7482
- block,
7483
- change: null
7368
+ function checkOf$1(cli, install, runAs, { bin, node }) {
7369
+ let survey;
7370
+ const surveyed = (cwd) => {
7371
+ survey ??= surveyDependency(install, bin, cwd);
7372
+ return survey;
7484
7373
  };
7485
7374
  return {
7486
- path,
7487
- block,
7488
- change: existing.includes(markersOf(cli.name).begin) ? "rewrite" : "add"
7375
+ install: async (ctx) => installFinding(cli, runAs, install, await surveyed(ctx.cwd)),
7376
+ release: async (ctx) => {
7377
+ const { cwd, root, standing } = await surveyed(ctx.cwd);
7378
+ if (isUnchecked(standing)) return { skipped: skippedFor(cli, install, standing) };
7379
+ return {
7380
+ npm: npmContext(cli, registryOf(install, root?.dir), ctx, node, root?.dir ?? cwd),
7381
+ advise: (latest) => adviceFor(install, root, latest)
7382
+ };
7383
+ }
7489
7384
  };
7490
7385
  }
7491
- async function handEditsAt(root) {
7492
- const claudeMd = join(root, "CLAUDE.md");
7493
- const text = await orMissing(readFile(claudeMd, "utf8"));
7494
- if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
7495
- return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
7496
- }
7497
- function agentsRow(cli) {
7386
+ /** The Install of a CLI each project pins as a dependency, run through npx (ADR 0011). */
7387
+ function dependencyInstall(cli, install) {
7388
+ const runAs = runAsOf(cli);
7498
7389
  return {
7499
- name: "agents",
7500
- advisory: true,
7501
- run: async (ctx) => {
7502
- const root = await projectRootOf(ctx.cwd);
7503
- const { path, change } = await agentsMdAt(cli, root);
7504
- const fix = `${runAsOf(cli)} skill install`;
7505
- if (change === "rewrite") return {
7506
- state: "problem",
7507
- message: `${path} holds a ${cli.name} block other than the one ${fix} writes`,
7508
- fix
7509
- };
7510
- if (change !== null) return {
7511
- state: "problem",
7512
- message: `${path} holds no ${cli.name} block, so sessions in this repo do not know ${cli.name}`,
7513
- fix
7514
- };
7515
- const holds = `${path} holds the ${cli.name} block`;
7516
- const [edit] = await handEditsAt(root);
7517
- return edit === void 0 ? {
7518
- state: "ok",
7519
- message: holds
7520
- } : {
7521
- state: "problem",
7522
- message: `${holds}; ${edit}`
7523
- };
7524
- }
7390
+ runAs,
7391
+ builtIns: () => ({}),
7392
+ exitClasses: [],
7393
+ agentsCaveat: `and only from a package that has \`${install.package}\` in its dependencies; from anywhere else npx reports the package missing`,
7394
+ check: (launch) => checkOf$1(cli, install, runAs, launch)
7525
7395
  };
7526
7396
  }
7527
7397
  //#endregion
7528
- //#region ../agent-cli/dist/built-ins/skills.js
7398
+ //#region ../agent-cli/dist/built-ins/skill/hub.js
7529
7399
  const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7530
7400
  const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
7531
7401
  const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
@@ -7583,473 +7453,168 @@ function fixFor(cliName, copies, source) {
7583
7453
  if (has("unversioned") && add !== void 0) return has("behind") ? `${update} && ${add}` : add;
7584
7454
  return has("behind") || has("unversioned") ? update : void 0;
7585
7455
  }
7586
- async function skillReport(cli, version, ctx) {
7587
- const copies = await skillCopies(cli.name, version, ctx);
7588
- const source = cli.skill !== void 0 && "source" in cli.skill ? cli.skill.source : void 0;
7589
- return {
7590
- copies,
7591
- fix: fixFor(cli.name, copies, source)
7592
- };
7593
- }
7594
- //#endregion
7595
- //#region ../agent-cli/dist/built-ins/packaged-skill.js
7596
- /** The folder, relative to the package root, of a CLI's Packaged skill, if it declares one. */
7597
- function packagedFolderOf(cli) {
7598
- return cli.skill !== void 0 && "packaged" in cli.skill ? cli.skill.packaged : void 0;
7456
+ const SKILL_STATE_NOTES = {
7457
+ current: "the same as this build",
7458
+ behind: "older than this build",
7459
+ ahead: "newer than this build",
7460
+ unversioned: "states no metadata.version",
7461
+ unreadable: "could not be read"
7462
+ };
7463
+ function describeCopy({ path, version, state }) {
7464
+ return `${path}${version === void 0 ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
7599
7465
  }
7600
- /** The project root and the Skill copy under it, as `skill install` writes it. */
7601
- async function skillCopyOf(cli, cwd) {
7602
- const root = await projectRootOf(cwd);
7466
+ /** A skill that `npx skills add` installs from `source`, the Skill Hub; with no `source`, no line adds it. */
7467
+ function hubSkill(cli, source) {
7468
+ const findingAt = async (version, ctx) => {
7469
+ const copies = await skillCopies(cli.name, version, ctx);
7470
+ const fix = fixFor(cli.name, copies, source);
7471
+ const message = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
7472
+ return fix === void 0 ? {
7473
+ state: "ok",
7474
+ message
7475
+ } : {
7476
+ state: "ok",
7477
+ message,
7478
+ fix
7479
+ };
7480
+ };
7603
7481
  return {
7604
- root,
7605
- copy: join(root, ".claude", "skills", cli.name)
7482
+ kind: "hub",
7483
+ builtIns: {},
7484
+ rows: [{
7485
+ name: "skill",
7486
+ advisory: true,
7487
+ run: (ctx) => findingAt(cli.version, ctx)
7488
+ }],
7489
+ notice: async () => {},
7490
+ findingAt
7606
7491
  };
7607
7492
  }
7608
- /** Every file under `dir`, relative to it and sorted; none when `dir` is missing. */
7609
- async function filesIn(dir) {
7610
- return (await orMissing(readdir(dir, {
7611
- recursive: true,
7612
- withFileTypes: true
7613
- })) ?? []).filter((entry) => entry.isFile()).map((entry) => relative(dir, join(entry.parentPath, entry.name))).sort();
7493
+ //#endregion
7494
+ //#region ../agent-cli/dist/built-ins/agents-md.js
7495
+ /** One paragraph telling every session that the CLI exists, how to run it and which skill to load. */
7496
+ function agentsBlock(cli, { runAs, agentsCaveat }) {
7497
+ const what = cli.description.replace(/\.$/, "");
7498
+ const run = `Run it as \`${runAs}\`${agentsCaveat === void 0 ? "" : `, ${agentsCaveat}`}`;
7499
+ return `\`${cli.name}\`: ${what}. ${run}. Load the \`${cli.name}\` skill before using it.`;
7614
7500
  }
7615
- /** The versions of the package's skill in `source` and of its copy in `copy`. */
7616
- async function skillVersionsOf(source, copy) {
7617
- const [shipped, installed] = await Promise.all([readFile(join(source, "SKILL.md"), "utf8"), orMissing(readFile(join(copy, "SKILL.md"), "utf8"))]);
7618
- const packageVersion = skillVersionIn(shipped) ?? null;
7619
- const copyVersion = installed === void 0 ? void 0 : skillVersionIn(installed) ?? null;
7501
+ function markersOf(name) {
7620
7502
  return {
7621
- packageVersion,
7622
- copyVersion,
7623
- same: copyVersion === packageVersion
7503
+ begin: `<!-- BEGIN:${name} -->`,
7504
+ end: `<!-- END:${name} -->`
7624
7505
  };
7625
7506
  }
7626
- async function personalCopyOf(name, copy, ctx) {
7627
- const home = join(homeOf(ctx.env), ".claude", "skills", name);
7628
- const real = await orMissing(realpath(home));
7629
- return real === void 0 || real === await orMissing(realpath(copy)) ? void 0 : home;
7630
- }
7631
- /** What the `skill` row of a Packaged skill carries under `--json` beside its message. */
7632
- const packagedSkillDetailsSchema = object({
7633
- copy: string(),
7634
- copyVersion: string().nullable(),
7635
- packageVersion: string().nullable(),
7636
- /** A personal copy, which Claude Code prefers over the project's. */
7637
- home: string().optional()
7638
- });
7639
- function versionLabel(version) {
7640
- return version ?? "(no metadata.version)";
7641
- }
7642
- function compared(name, { copy, copyVersion, packageVersion }, same) {
7643
- const stated = `${copy} ${versionLabel(copyVersion)}`;
7644
- return same ? `${stated}, the version ${name} ships` : `${stated} differs from the ${versionLabel(packageVersion)} that ${name} ships`;
7507
+ /** `text` with the CLI's block between its markers, appended when they are missing; nothing else changes. */
7508
+ function withBlock(cli, install, text) {
7509
+ const { begin, end } = markersOf(cli.name);
7510
+ const whole = `${begin}\n${agentsBlock(cli, install)}\n${end}`;
7511
+ const start = text.indexOf(begin);
7512
+ const stop = start === -1 ? -1 : text.indexOf(end, start);
7513
+ if (stop !== -1) return `${text.slice(0, start)}${whole}${text.slice(stop + end.length)}`;
7514
+ if (text === "") return `${whole}\n`;
7515
+ return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
7645
7516
  }
7646
- /** The `skill` row of a Packaged skill: the project root's copy against the package's, and any under home. */
7647
- async function packagedSkillFinding(cli, folder, ctx) {
7648
- const { copy } = await skillCopyOf(cli, ctx.cwd);
7649
- const versions = await skillVersionsOf(join(ctx.packageRoot, folder), copy);
7650
- const { packageVersion } = versions;
7651
- const copyVersion = versions.copyVersion ?? null;
7652
- const home = await personalCopyOf(cli.name, copy, ctx);
7653
- const skill = {
7654
- copy,
7655
- copyVersion,
7656
- packageVersion,
7657
- ...home === void 0 ? {} : { home }
7658
- };
7659
- const head = versions.copyVersion === void 0 ? `no copy of the ${cli.name} skill at ${copy}` : compared(cli.name, skill, versions.same);
7660
- const message = home === void 0 ? head : `${head}; remove ${home}: Claude Code prefers a personal skill over the project's`;
7661
- if (!versions.same) return {
7662
- state: "problem",
7663
- message,
7664
- fix: `${runAsOf(cli)} skill install`,
7665
- skill
7666
- };
7667
- return home === void 0 ? {
7668
- state: "ok",
7669
- message,
7670
- skill
7671
- } : {
7672
- state: "problem",
7673
- message,
7674
- skill
7517
+ async function agentsMdAt(cli, install, root) {
7518
+ const path = join(root, "AGENTS.md");
7519
+ const existing = await orMissing(readFile(path, "utf8"));
7520
+ const block = agentsBlock(cli, install);
7521
+ if (existing === void 0) return {
7522
+ path,
7523
+ block,
7524
+ change: "create"
7675
7525
  };
7676
- }
7677
- /** Gives a Packaged skill's `skill` row problem as a Notice, since an agent rarely runs `doctor`. */
7678
- async function noticeSkillCopy(cli, { builtIn, bin }, ctx) {
7679
- if (builtIn || bin === void 0 || cli.commands === void 0) return;
7680
- const folder = packagedFolderOf(cli);
7681
- if (folder === void 0) return;
7682
- let finding;
7683
- try {
7684
- finding = await packagedSkillFinding(cli, folder, ctx);
7685
- } catch (error) {
7686
- if (errnoOf(error) === void 0) throw error;
7687
- ctx.notice(`could not check the ${cli.name} skill: ${messageOf(error)}`);
7688
- return;
7689
- }
7690
- if (finding.state !== "problem") return;
7691
- ctx.notice(finding.fix === void 0 ? finding.message : `${finding.message}; run \`${finding.fix}\``);
7692
- }
7693
- //#endregion
7694
- //#region ../agent-cli/dist/built-ins/doctor-rows.js
7695
- function ok(message) {
7696
- return {
7697
- state: "ok",
7698
- message
7526
+ if (withBlock(cli, install, existing) === existing) return {
7527
+ path,
7528
+ block,
7529
+ change: null
7699
7530
  };
7700
- }
7701
- function noInstall(cli) {
7702
7531
  return {
7703
- state: "skipped",
7704
- message: `not checked: ${cli.name} declares no install`
7532
+ path,
7533
+ block,
7534
+ change: existing.includes(markersOf(cli.name).begin) ? "rewrite" : "add"
7705
7535
  };
7706
7536
  }
7707
- function buildRow(cli) {
7708
- return {
7709
- name: "build",
7710
- run: () => ok(versionLine(cli))
7711
- };
7537
+ async function handEditsAt(root) {
7538
+ const claudeMd = join(root, "CLAUDE.md");
7539
+ const text = await orMissing(readFile(claudeMd, "utf8"));
7540
+ if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
7541
+ return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
7712
7542
  }
7713
- function nodeRow(cli) {
7543
+ function agentsRow(cli, install) {
7714
7544
  return {
7715
- name: "node",
7716
- run: () => {
7717
- const found = process.versions.node;
7718
- const range = cli.engines?.node;
7719
- if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
7720
- const minimum = engineMinimum(range);
7721
- if (minimum === void 0) return {
7545
+ name: "agents",
7546
+ advisory: true,
7547
+ run: async (ctx) => {
7548
+ const root = await projectRootOf(ctx.cwd);
7549
+ const { path, change } = await agentsMdAt(cli, install, root);
7550
+ const fix = `${install.runAs} skill install`;
7551
+ if (change === "rewrite") return {
7722
7552
  state: "problem",
7723
- message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
7553
+ message: `${path} holds a ${cli.name} block other than the one ${fix} writes`,
7554
+ fix
7724
7555
  };
7725
- if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
7726
- const major = minimum[0];
7727
- return {
7556
+ if (change !== null) return {
7728
7557
  state: "problem",
7729
- message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
7730
- fix: `nvm install ${major} && nvm alias default ${major}`
7558
+ message: `${path} holds no ${cli.name} block, so sessions in this repo do not know ${cli.name}`,
7559
+ fix
7560
+ };
7561
+ const holds = `${path} holds the ${cli.name} block`;
7562
+ const [edit] = await handEditsAt(root);
7563
+ return edit === void 0 ? {
7564
+ state: "ok",
7565
+ message: holds
7566
+ } : {
7567
+ state: "problem",
7568
+ message: `${holds}; ${edit}`
7731
7569
  };
7732
7570
  }
7733
7571
  };
7734
7572
  }
7735
- /** A row's thrown error as its problem: a `CliError` with its hint and code, anything else a bug. */
7736
- function problemOfError(name, error) {
7737
- if (error instanceof CliError) return {
7738
- state: "problem",
7739
- message: `${error.message}; ${error.hint}`,
7740
- code: error.code
7741
- };
7742
- return {
7743
- state: "problem",
7744
- message: `the ${name} row hit a bug: ${messageOf(error)}`
7745
- };
7746
- }
7747
- function readProject(projectFile, ctx) {
7748
- if (projectFile === void 0) return {
7749
- ok: true,
7750
- project: void 0
7751
- };
7752
- try {
7753
- return {
7754
- ok: true,
7755
- project: projectOf(projectFile, ctx.cwd, {
7756
- fileError: ctx.fileError,
7757
- callerLine: ctx.commandLine()
7758
- })
7759
- };
7760
- } catch (error) {
7761
- return {
7762
- ok: false,
7763
- error
7764
- };
7765
- }
7573
+ //#endregion
7574
+ //#region ../agent-cli/dist/agent.js
7575
+ const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
7576
+ const isSet = (name) => (env) => (env[name] ?? "") !== "";
7577
+ const equals = (name, value) => (env) => env[name] === value;
7578
+ const MARKERS = [
7579
+ (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
7580
+ equals("AGENT", "amp"),
7581
+ isSet("CODEX_SANDBOX"),
7582
+ isSet("CODEX_CI"),
7583
+ isSet("CODEX_THREAD_ID"),
7584
+ isSet("GEMINI_CLI"),
7585
+ isSet("COPILOT_CLI"),
7586
+ isSet("OPENCODE"),
7587
+ isSet("ANTIGRAVITY_AGENT"),
7588
+ isSet("AUGMENT_AGENT"),
7589
+ isSet("REPL_ID"),
7590
+ isSet("CLAUDE_CODE_IS_COWORK"),
7591
+ isSet("CLAUDECODE"),
7592
+ isSet("CLAUDE_CODE"),
7593
+ isSet("CURSOR_TRACE_ID"),
7594
+ isSet("CURSOR_AGENT"),
7595
+ equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
7596
+ equals("TERM_PROGRAM", "kiro"),
7597
+ (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
7598
+ isSet("GOOSE_PROVIDER")
7599
+ ];
7600
+ /**
7601
+ * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
7602
+ * in a PTY, so a TTY alone doesn't mean a person is watching.
7603
+ */
7604
+ function isAgent(env) {
7605
+ return MARKERS.some((marker) => marker(env));
7766
7606
  }
7767
- function projectRow({ file }, read) {
7768
- return {
7769
- name: "project",
7770
- run: (ctx) => {
7771
- if (!read.ok) return problemOfError("project", read.error);
7772
- return ok(read.project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(read.project.root, file));
7773
- }
7774
- };
7775
- }
7776
- function surveyOnce(bin) {
7777
- let survey;
7778
- return (install, cwd) => {
7779
- survey ??= surveyDependency(install, bin, cwd);
7780
- return survey;
7781
- };
7782
- }
7783
- function installRow(cli, { bin, node }, survey) {
7784
- return {
7785
- name: "install",
7786
- advisory: true,
7787
- run: async (ctx) => {
7788
- const { install } = cli;
7789
- if (install === void 0) return noInstall(cli);
7790
- if (install.mode === "dependency") return dependencyInstallFinding(cli, install, await survey(install, ctx.cwd));
7791
- const npm = globalNpm(cli, install, ctx, node);
7792
- try {
7793
- return ok(`a global npm install under ${(await globalInstallOf(bin, npm)).prefix}`);
7794
- } catch (error) {
7795
- if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
7796
- state: "problem",
7797
- message: error.message,
7798
- fix: installCommandLine(npm.registry)
7799
- };
7800
- throw error;
7801
- }
7802
- }
7803
- };
7804
- }
7805
- async function releaseLookupOf(cli, install, ctx, node, survey) {
7806
- if (install.mode === "global") {
7807
- const fix = `${cli.name} update`;
7808
- return {
7809
- npm: globalNpm(cli, install, ctx, node),
7810
- advise: () => ({ fix })
7811
- };
7812
- }
7813
- const release = dependencyRelease(cli, install, await survey(install, ctx.cwd));
7814
- if ("skipped" in release) return release;
7815
- return {
7816
- npm: npmContext(cli, release.registry, ctx, node, release.cwd),
7817
- advise: release.advise
7818
- };
7819
- }
7820
- function registryRow(cli, { node }, survey) {
7821
- return {
7822
- name: "registry",
7823
- advisory: true,
7824
- run: async (ctx) => {
7825
- const { install } = cli;
7826
- if (install === void 0) return noInstall(cli);
7827
- const lookup = await releaseLookupOf(cli, install, ctx, node, survey);
7828
- if ("skipped" in lookup) return lookup.skipped;
7829
- const { npm, advise } = lookup;
7830
- const latest = await latestVersion(npm);
7831
- const standing = standingOf$1(cli.version, latest);
7832
- const head = `${npm.registry.label} has ${latest} as latest: this build`;
7833
- if (standing === "behind") {
7834
- const message = `${head} (${cli.version}) is older`;
7835
- const advice = advise(latest);
7836
- return "fix" in advice ? {
7837
- state: "problem",
7838
- message,
7839
- fix: advice.fix
7840
- } : {
7841
- state: "problem",
7842
- message: `${message}; ${advice.note}`
7843
- };
7844
- }
7845
- return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
7846
- }
7847
- };
7848
- }
7849
- const SKILL_STATE_NOTES = {
7850
- current: "the same as this build",
7851
- behind: "older than this build",
7852
- ahead: "newer than this build",
7853
- unversioned: "states no metadata.version",
7854
- unreadable: "could not be read"
7855
- };
7856
- function describeCopy({ path, version, state }) {
7857
- return `${path}${version === void 0 ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
7858
- }
7859
- function skillRow(cli) {
7860
- const packaged = packagedFolderOf(cli);
7861
- return {
7862
- name: "skill",
7863
- advisory: true,
7864
- run: async (ctx) => {
7865
- if (packaged !== void 0) return packagedSkillFinding(cli, packaged, ctx);
7866
- const { copies, fix } = await skillReport(cli, cli.version, ctx);
7867
- const message = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
7868
- return fix === void 0 ? ok(message) : {
7869
- state: "ok",
7870
- message,
7871
- fix
7872
- };
7873
- }
7874
- };
7875
- }
7876
- /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
7877
- function doctorChecks(cli, launch, read) {
7878
- const survey = surveyOnce(launch.bin);
7879
- return [
7880
- buildRow(cli),
7881
- nodeRow(cli),
7882
- ...cli.project === void 0 ? [] : [projectRow(cli.project, read)],
7883
- ...cli.doctor ?? [],
7884
- ...toolRows(cli.tools ?? [], cli.commands),
7885
- installRow(cli, launch, survey),
7886
- registryRow(cli, launch, survey),
7887
- skillRow(cli),
7888
- ...packagedFolderOf(cli) === void 0 ? [] : [agentsRow(cli)]
7889
- ];
7890
- }
7891
- //#endregion
7892
- //#region ../agent-cli/dist/built-ins/doctor.js
7893
- const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
7894
- const DEFAULT_DEADLINE_MS = 1e4;
7895
- const MS_PER_SECOND = 1e3;
7896
- const MIN_LABEL_WIDTH = 8;
7897
- const reportSchema = object({
7898
- rows: array(object({
7899
- check: string(),
7900
- state: _enum([
7901
- "ok",
7902
- "problem",
7903
- "skipped"
7904
- ]),
7905
- message: string(),
7906
- fix: string().optional(),
7907
- code: string().optional(),
7908
- skill: packagedSkillDetailsSchema.optional()
7909
- })),
7910
- next: array(string())
7911
- });
7912
- function problemOf(check, error, { signal, deadline, ms }) {
7913
- if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
7914
- state: "problem",
7915
- message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
7916
- };
7917
- return problemOfError(check.name, error);
7918
- }
7919
- function rowContext(ctx, signal, read) {
7920
- return Object.create(ctx, {
7921
- signal: { value: signal },
7922
- project: { get: () => {
7923
- if (!read.ok) throw read.error;
7924
- return read.project;
7925
- } }
7926
- });
7927
- }
7928
- async function rowOf(check, ctx, read) {
7929
- const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
7930
- const signal = ctx.deadline(ms);
7931
- let finding;
7932
- try {
7933
- finding = await Promise.race([Promise.resolve().then(() => check.run(rowContext(ctx, signal, read))), untilAborted(signal)]);
7934
- } catch (error) {
7935
- if (ctx.signal.aborted) return void 0;
7936
- finding = !read.ok && error === read.error ? {
7937
- state: "skipped",
7938
- message: "the Project file is unusable; the project row says why"
7939
- } : problemOf(check, error, {
7940
- signal: ctx.signal,
7941
- deadline: signal,
7942
- ms
7943
- });
7944
- }
7945
- return {
7946
- row: {
7947
- check: check.name,
7948
- ...finding
7949
- },
7950
- advisory: check.advisory === true
7951
- };
7952
- }
7953
- function nextOf(rows) {
7954
- return [...new Set(rows.flatMap(({ fix }) => fix === void 0 ? [] : [fix]))];
7955
- }
7956
- function render({ rows, next }, style) {
7957
- const width = Math.max(MIN_LABEL_WIDTH, ...rows.map(({ check }) => check.length)) + 2;
7958
- const stateWidth = Math.max(...rows.map(({ state }) => state.length)) + 2;
7959
- return [...rows.map(({ check, state, message }) => {
7960
- const label = state === "problem" ? style.red(state) : state;
7961
- return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
7962
- }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
7963
- }
7964
- function failure(cliName, callerLine, problems) {
7965
- const errors = problems.map(({ check, message, fix, code }) => new CliError({
7966
- code: code ?? "DOCTOR_PROBLEMS",
7967
- message: `${cliName} doctor found a problem in ${check}: ${message}`,
7968
- hint: fix === void 0 ? "the row says what is wrong" : `run \`${fix}\`, then run \`${callerLine}\` again`
7969
- }));
7970
- return new AggregateError(errors);
7971
- }
7972
- /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
7973
- function doctorCommand(cli, launch) {
7974
- return defineCommand$1({
7975
- name: "doctor",
7976
- description: "check the setup and say what fixes each problem",
7977
- examples: [{
7978
- command: `${runAsOf(cli)} doctor`,
7979
- note: "check the setup and list the commands that fix it"
7980
- }, {
7981
- command: `${runAsOf(cli)} doctor --json`,
7982
- note: "get the rows and fixes as data"
7983
- }],
7984
- effect: "read",
7985
- output: reportSchema,
7986
- run: async (_input, ctx) => {
7987
- const read = readProject(cli.project, ctx);
7988
- const checked = (await Promise.all(doctorChecks(cli, launch, read).map((check) => rowOf(check, ctx, read)))).filter((row) => row !== void 0);
7989
- const rows = checked.map(({ row }) => row);
7990
- const report = {
7991
- rows,
7992
- next: nextOf(rows)
7993
- };
7994
- if (ctx.signal.aborted) {
7995
- ctx.deliver(report);
7996
- ctx.signal.throwIfAborted();
7997
- }
7998
- const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
7999
- if (problems.length > 0) {
8000
- ctx.deliver(report);
8001
- throw failure(cli.name, ctx.commandLine(), problems);
8002
- }
8003
- return report;
8004
- },
8005
- render
8006
- });
8007
- }
8008
- //#endregion
8009
- //#region ../agent-cli/dist/agent.js
8010
- const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
8011
- const isSet = (name) => (env) => (env[name] ?? "") !== "";
8012
- const equals = (name, value) => (env) => env[name] === value;
8013
- const MARKERS = [
8014
- (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
8015
- equals("AGENT", "amp"),
8016
- isSet("CODEX_SANDBOX"),
8017
- isSet("CODEX_CI"),
8018
- isSet("CODEX_THREAD_ID"),
8019
- isSet("GEMINI_CLI"),
8020
- isSet("COPILOT_CLI"),
8021
- isSet("OPENCODE"),
8022
- isSet("ANTIGRAVITY_AGENT"),
8023
- isSet("AUGMENT_AGENT"),
8024
- isSet("REPL_ID"),
8025
- isSet("CLAUDE_CODE_IS_COWORK"),
8026
- isSet("CLAUDECODE"),
8027
- isSet("CLAUDE_CODE"),
8028
- isSet("CURSOR_TRACE_ID"),
8029
- isSet("CURSOR_AGENT"),
8030
- equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
8031
- equals("TERM_PROGRAM", "kiro"),
8032
- (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
8033
- isSet("GOOSE_PROVIDER")
8034
- ];
8035
- /**
8036
- * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
8037
- * in a PTY, so a TTY alone doesn't mean a person is watching.
8038
- */
8039
- function isAgent(env) {
8040
- return MARKERS.some((marker) => marker(env));
8041
- }
8042
- //#endregion
8043
- //#region ../agent-cli/dist/errno.js
8044
- const PERMISSION_DENIED = "PERMISSION_DENIED";
8045
- const DISK_FULL = "DISK_FULL";
8046
- const NOT_FOUND = "NOT_FOUND";
8047
- const ALREADY_EXISTS = "ALREADY_EXISTS";
8048
- const NOT_A_FILE = "NOT_A_FILE";
8049
- function permissionHint({ path, access, callerLine, env, platform }) {
8050
- if (platform === "win32") return `a program may hold ${path} open: close it, then run \`${callerLine}\` again`;
8051
- if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
8052
- return `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`;
7607
+ //#endregion
7608
+ //#region ../agent-cli/dist/errno.js
7609
+ const PERMISSION_DENIED = "PERMISSION_DENIED";
7610
+ const DISK_FULL = "DISK_FULL";
7611
+ const NOT_FOUND = "NOT_FOUND";
7612
+ const ALREADY_EXISTS = "ALREADY_EXISTS";
7613
+ const NOT_A_FILE = "NOT_A_FILE";
7614
+ function permissionHint({ path, access, callerLine, env, platform }) {
7615
+ if (platform === "win32") return `a program may hold ${path} open: close it, then run \`${callerLine}\` again`;
7616
+ if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
7617
+ return `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`;
8053
7618
  }
8054
7619
  function fileErrorOf(scope) {
8055
7620
  return (error, at) => classifyErrno(error, {
@@ -8337,22 +7902,48 @@ async function probeOutputs({ files, cwd, fileError }, outputs) {
8337
7902
  }));
8338
7903
  }
8339
7904
  //#endregion
8340
- //#region ../agent-cli/dist/built-ins/skill-install.js
8341
- const installSchema = object({
8342
- copy: string(),
8343
- written: array(string()),
8344
- removed: array(string()),
8345
- agentsMd: object({
8346
- path: string(),
8347
- changed: boolean()
8348
- }),
8349
- handEdits: array(string())
8350
- });
8351
- async function sameContent(left, right) {
8352
- const [a, b] = await Promise.all([readFile(left), readFile(right)]);
7905
+ //#region ../agent-cli/dist/built-ins/skill/install-command.js
7906
+ /** The project root and the Skill copy under it, as `skill install` writes it. */
7907
+ async function skillCopyOf(cli, cwd) {
7908
+ const root = await projectRootOf(cwd);
7909
+ return {
7910
+ root,
7911
+ copy: join(root, ".claude", "skills", cli.name)
7912
+ };
7913
+ }
7914
+ /** Every file under `dir`, relative to it and sorted; none when `dir` is missing. */
7915
+ async function filesIn(dir) {
7916
+ return (await orMissing(readdir(dir, {
7917
+ recursive: true,
7918
+ withFileTypes: true
7919
+ })) ?? []).filter((entry) => entry.isFile()).map((entry) => relative(dir, join(entry.parentPath, entry.name))).sort();
7920
+ }
7921
+ /** The versions of the package's skill in `source` and of its copy in `copy`. */
7922
+ async function skillVersionsOf(source, copy) {
7923
+ const [shipped, installed] = await Promise.all([readFile(join(source, "SKILL.md"), "utf8"), orMissing(readFile(join(copy, "SKILL.md"), "utf8"))]);
7924
+ const packageVersion = skillVersionIn(shipped) ?? null;
7925
+ const copyVersion = installed === void 0 ? void 0 : skillVersionIn(installed) ?? null;
7926
+ return {
7927
+ packageVersion,
7928
+ copyVersion,
7929
+ same: copyVersion === packageVersion
7930
+ };
7931
+ }
7932
+ const installSchema = object({
7933
+ copy: string(),
7934
+ written: array(string()),
7935
+ removed: array(string()),
7936
+ agentsMd: object({
7937
+ path: string(),
7938
+ changed: boolean()
7939
+ }),
7940
+ handEdits: array(string())
7941
+ });
7942
+ async function sameContent(left, right) {
7943
+ const [a, b] = await Promise.all([readFile(left), readFile(right)]);
8353
7944
  return a.equals(b);
8354
7945
  }
8355
- async function planOf(cli, folder, cwd, packageRoot) {
7946
+ async function planOf(cli, folder, install, cwd, packageRoot) {
8356
7947
  const source = join(packageRoot, folder);
8357
7948
  const { root, copy } = await skillCopyOf(cli, cwd);
8358
7949
  const [shipped, present] = await Promise.all([filesIn(source), filesIn(copy)]);
@@ -8364,7 +7955,7 @@ async function planOf(cli, folder, cwd, packageRoot) {
8364
7955
  replace: shipped.filter((_file, index) => differs[index]),
8365
7956
  remove: present.filter((file) => !shipped.includes(file)),
8366
7957
  sameVersion: (await skillVersionsOf(source, copy)).same,
8367
- agentsMd: await agentsMdAt(cli, root),
7958
+ agentsMd: await agentsMdAt(cli, install, root),
8368
7959
  handEdits: await handEditsAt(root)
8369
7960
  };
8370
7961
  }
@@ -8404,8 +7995,8 @@ function editedRemovals(plan, ctx) {
8404
7995
  });
8405
7996
  }
8406
7997
  /** The built-in `skill install`, for a CLI whose skill ships in its package. */
8407
- function skillInstallCommand(cli, folder) {
8408
- const runAs = runAsOf(cli);
7998
+ function skillInstallCommand(cli, folder, install) {
7999
+ const { runAs } = install;
8409
8000
  return defineCommand$1({
8410
8001
  name: "install",
8411
8002
  description: "copy this CLI's skill into the project and write its AGENTS.md block",
@@ -8419,7 +8010,7 @@ function skillInstallCommand(cli, folder) {
8419
8010
  effect: "write",
8420
8011
  output: installSchema,
8421
8012
  force: "replace or remove hand-edited files of a skill copy at the package's version",
8422
- plan: (_input, ctx) => planOf(cli, folder, ctx.cwd, ctx.packageRoot),
8013
+ plan: (_input, ctx) => planOf(cli, folder, install, ctx.cwd, ctx.packageRoot),
8423
8014
  outputs: (plan) => plan.sameVersion ? [...plan.write, ...plan.replace].map((file) => join(plan.copy, file)) : [],
8424
8015
  apply: async (plan, ctx) => {
8425
8016
  if (plan.sameVersion && plan.remove.length > 0 && !ctx.force) throw editedRemovals(plan, ctx);
@@ -8428,7 +8019,7 @@ function skillInstallCommand(cli, folder) {
8428
8019
  const { path, change } = plan.agentsMd;
8429
8020
  if (change !== null) {
8430
8021
  const existing = await orMissing(readFile(path, "utf8")) ?? "";
8431
- await ctx.files.write(path, withBlock(cli, existing));
8022
+ await ctx.files.write(path, withBlock(cli, install, existing));
8432
8023
  }
8433
8024
  return {
8434
8025
  copy: plan.copy,
@@ -8446,6 +8037,93 @@ function skillInstallCommand(cli, folder) {
8446
8037
  });
8447
8038
  }
8448
8039
  //#endregion
8040
+ //#region ../agent-cli/dist/built-ins/skill/packaged.js
8041
+ async function personalCopyOf(name, copy, ctx) {
8042
+ const home = join(homeOf(ctx.env), ".claude", "skills", name);
8043
+ const real = await orMissing(realpath(home));
8044
+ return real === void 0 || real === await orMissing(realpath(copy)) ? void 0 : home;
8045
+ }
8046
+ /** What the `skill` row of a Packaged skill carries under `--json` beside its message. */
8047
+ const packagedSkillDetailsSchema = object({
8048
+ copy: string(),
8049
+ copyVersion: string().nullable(),
8050
+ packageVersion: string().nullable(),
8051
+ /** A personal copy, which Claude Code prefers over the project's. */
8052
+ home: string().optional()
8053
+ });
8054
+ function versionLabel(version) {
8055
+ return version ?? "(no metadata.version)";
8056
+ }
8057
+ function compared(name, { copy, copyVersion, packageVersion }, same) {
8058
+ const stated = `${copy} ${versionLabel(copyVersion)}`;
8059
+ return same ? `${stated}, the version ${name} ships` : `${stated} differs from the ${versionLabel(packageVersion)} that ${name} ships`;
8060
+ }
8061
+ /** The project root's copy against the package's, and any under home. */
8062
+ async function findingOf(cli, folder, install, ctx) {
8063
+ const { copy } = await skillCopyOf(cli, ctx.cwd);
8064
+ const versions = await skillVersionsOf(join(ctx.packageRoot, folder), copy);
8065
+ const { packageVersion } = versions;
8066
+ const copyVersion = versions.copyVersion ?? null;
8067
+ const home = await personalCopyOf(cli.name, copy, ctx);
8068
+ const skill = {
8069
+ copy,
8070
+ copyVersion,
8071
+ packageVersion,
8072
+ ...home === void 0 ? {} : { home }
8073
+ };
8074
+ const head = versions.copyVersion === void 0 ? `no copy of the ${cli.name} skill at ${copy}` : compared(cli.name, skill, versions.same);
8075
+ const message = home === void 0 ? head : `${head}; remove ${home}: Claude Code prefers a personal skill over the project's`;
8076
+ if (!versions.same) return {
8077
+ state: "problem",
8078
+ message,
8079
+ fix: `${install.runAs} skill install`,
8080
+ skill
8081
+ };
8082
+ return home === void 0 ? {
8083
+ state: "ok",
8084
+ message,
8085
+ skill
8086
+ } : {
8087
+ state: "problem",
8088
+ message,
8089
+ skill
8090
+ };
8091
+ }
8092
+ async function notice(cli, folder, install, { builtIn, bin }, ctx) {
8093
+ if (builtIn || bin === void 0 || cli.commands === void 0) return;
8094
+ let finding;
8095
+ try {
8096
+ finding = await findingOf(cli, folder, install, ctx);
8097
+ } catch (error) {
8098
+ if (errnoOf(error) === void 0) throw error;
8099
+ ctx.notice(`could not check the ${cli.name} skill: ${messageOf(error)}`);
8100
+ return;
8101
+ }
8102
+ if (finding.state !== "problem") return;
8103
+ ctx.notice(finding.fix === void 0 ? finding.message : `${finding.message}; run \`${finding.fix}\``);
8104
+ }
8105
+ /** A skill shipped in the package at `folder`, which `skill install` copies to the project root. */
8106
+ function packagedSkill(cli, folder, install) {
8107
+ return {
8108
+ kind: "packaged",
8109
+ builtIns: { skill: { install: skillInstallCommand(cli, folder, install) } },
8110
+ rows: [{
8111
+ name: "skill",
8112
+ advisory: true,
8113
+ run: (ctx) => findingOf(cli, folder, install, ctx)
8114
+ }, agentsRow(cli, install)],
8115
+ notice: (launch, ctx) => notice(cli, folder, install, launch, ctx),
8116
+ findingAt: (_version, ctx) => findingOf(cli, folder, install, ctx)
8117
+ };
8118
+ }
8119
+ //#endregion
8120
+ //#region ../agent-cli/dist/built-ins/skill/skill.js
8121
+ /** A Packaged skill, or else a Skill Hub's, also for a CLI that declares no `skill`. */
8122
+ function skillOf(cli, install) {
8123
+ const { skill } = cli;
8124
+ return skill !== void 0 && "packaged" in skill ? packagedSkill(cli, skill.packaged, install) : hubSkill(cli, skill?.source);
8125
+ }
8126
+ //#endregion
8449
8127
  //#region ../agent-cli/dist/interrupts.js
8450
8128
  const INTERRUPTED = "INTERRUPTED";
8451
8129
  const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
@@ -8583,6 +8261,59 @@ var RunControl = class {
8583
8261
  }
8584
8262
  };
8585
8263
  //#endregion
8264
+ //#region ../agent-cli/dist/built-ins/install/global-npm.js
8265
+ const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
8266
+ /** Where npm puts global packages under its global prefix. */
8267
+ function globalModulesDir(prefix) {
8268
+ return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
8269
+ }
8270
+ function notGlobal(ctx, message) {
8271
+ return new CliError({
8272
+ code: NOT_GLOBAL_INSTALL,
8273
+ message,
8274
+ hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.registry)}`
8275
+ });
8276
+ }
8277
+ /**
8278
+ * The global npm install this copy runs from, found by asking npm for its global prefix.
8279
+ * `bin` is the script that started the CLI; throws `NOT_GLOBAL_INSTALL` when it is not inside that install.
8280
+ */
8281
+ async function globalInstallOf(bin, ctx) {
8282
+ if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
8283
+ const prefix = await globalPrefix(ctx);
8284
+ const packageDir = join(globalModulesDir(prefix), ctx.registry.package);
8285
+ const installed = await orMissing(realpath(packageDir));
8286
+ const here = await orMissing(realpath(bin)) ?? bin;
8287
+ if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
8288
+ if ((await lstat(packageDir)).isSymbolicLink()) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
8289
+ return {
8290
+ prefix,
8291
+ packageDir
8292
+ };
8293
+ }
8294
+ const NPMJS = "https://registry.npmjs.org/";
8295
+ /** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
8296
+ function registryFlags({ package: name, registry }) {
8297
+ if (registry === NPMJS) return [];
8298
+ const scope = scopeOf(name);
8299
+ return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
8300
+ }
8301
+ function globalRegistry(install) {
8302
+ const { package: name, registry } = install;
8303
+ return {
8304
+ package: name,
8305
+ label: registry,
8306
+ flags: registryFlags(install),
8307
+ unreachableHint: "check the network or VPN",
8308
+ loginHint: `log in to ${registry} (\`npm login --registry=${registry}\`) or set its token in ~/.npmrc`,
8309
+ missingHint: `check that ${name} is published on ${registry}`
8310
+ };
8311
+ }
8312
+ /** npm for a Global install, run in the working directory. */
8313
+ function globalNpm(cli, install, ctx, node) {
8314
+ return npmContext(cli, globalRegistry(install), ctx, node, ctx.cwd);
8315
+ }
8316
+ //#endregion
8586
8317
  //#region ../agent-cli/dist/built-ins/upgrades.js
8587
8318
  /** A skipped release's Upgrade steps, or its whole section when it lists none. */
8588
8319
  const upgradeSchema = union([object({
@@ -8675,10 +8406,9 @@ function renderUpdate(cli, install, update) {
8675
8406
  ...next
8676
8407
  ]).join("\n");
8677
8408
  }
8678
- async function skillNext(cli, latest, ctx) {
8679
- const packaged = packagedFolderOf(cli);
8680
- const finding = packaged === void 0 ? await skillReport(cli, latest, ctx) : await packagedSkillFinding(cli, packaged, ctx);
8681
- return "fix" in finding && finding.fix !== void 0 ? [finding.fix] : [];
8409
+ async function skillNext(skill, latest, ctx) {
8410
+ const { fix } = await skill.findingAt(latest, ctx);
8411
+ return fix === void 0 ? [] : [fix];
8682
8412
  }
8683
8413
  function interruptedAfterInstall(plan) {
8684
8414
  return new CliError({
@@ -8688,7 +8418,7 @@ function interruptedAfterInstall(plan) {
8688
8418
  });
8689
8419
  }
8690
8420
  /** The built-in `update`, for a CLI installed globally with npm. */
8691
- function updateCommand(cli, install, { bin, node }) {
8421
+ function updateCommand(cli, install, skill, { bin, node }) {
8692
8422
  return defineCommand$1({
8693
8423
  name: "update",
8694
8424
  description: "install the latest version of this CLI from its registry",
@@ -8719,7 +8449,7 @@ function updateCommand(cli, install, { bin, node }) {
8719
8449
  latest,
8720
8450
  installed: null,
8721
8451
  upgrades: [],
8722
- next: await skillNext(cli, latest, ctx)
8452
+ next: await skillNext(skill, latest, ctx)
8723
8453
  };
8724
8454
  ctx.signal.throwIfAborted();
8725
8455
  ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
@@ -8732,7 +8462,7 @@ function updateCommand(cli, install, { bin, node }) {
8732
8462
  prefix
8733
8463
  },
8734
8464
  upgrades: await upgradesOf(plan.packageDir, from, latest),
8735
- next: await skillNext(cli, latest, ctx)
8465
+ next: await skillNext(skill, latest, ctx)
8736
8466
  };
8737
8467
  if (ctx.signal.aborted) {
8738
8468
  ctx.deliver(update);
@@ -8745,17 +8475,323 @@ function updateCommand(cli, install, { bin, node }) {
8745
8475
  });
8746
8476
  }
8747
8477
  //#endregion
8478
+ //#region ../agent-cli/dist/built-ins/install/global.js
8479
+ function checkOf(cli, install, { bin, node }) {
8480
+ return {
8481
+ install: async (ctx) => {
8482
+ const npm = globalNpm(cli, install, ctx, node);
8483
+ try {
8484
+ return {
8485
+ state: "ok",
8486
+ message: `a global npm install under ${(await globalInstallOf(bin, npm)).prefix}`
8487
+ };
8488
+ } catch (error) {
8489
+ if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
8490
+ state: "problem",
8491
+ message: error.message,
8492
+ fix: installCommandLine(npm.registry)
8493
+ };
8494
+ throw error;
8495
+ }
8496
+ },
8497
+ release: async (ctx) => {
8498
+ const fix = `${cli.name} update`;
8499
+ return {
8500
+ npm: globalNpm(cli, install, ctx, node),
8501
+ advise: () => ({ fix })
8502
+ };
8503
+ }
8504
+ };
8505
+ }
8506
+ /** The Install of a CLI installed once per machine with npm, which `update` replaces. */
8507
+ function globalInstall(cli, install) {
8508
+ const adapter = {
8509
+ runAs: runAsOf(cli),
8510
+ builtIns: (launch) => ({ update: updateCommand(cli, install, skillOf(cli, adapter), launch) }),
8511
+ exitClasses: ["source"],
8512
+ agentsCaveat: void 0,
8513
+ check: (launch) => checkOf(cli, install, launch)
8514
+ };
8515
+ return adapter;
8516
+ }
8517
+ //#endregion
8518
+ //#region ../agent-cli/dist/built-ins/install/install.js
8519
+ function noInstall(cli) {
8520
+ const skipped = async () => ({
8521
+ state: "skipped",
8522
+ message: `not checked: ${cli.name} declares no install`
8523
+ });
8524
+ return {
8525
+ runAs: runAsOf(cli),
8526
+ builtIns: () => ({}),
8527
+ exitClasses: [],
8528
+ agentsCaveat: void 0,
8529
+ check: () => ({
8530
+ install: skipped,
8531
+ release: async () => ({ skipped: await skipped() })
8532
+ })
8533
+ };
8534
+ }
8535
+ function installOf(cli) {
8536
+ const { install } = cli;
8537
+ if (install === void 0) return noInstall(cli);
8538
+ return install.mode === "global" ? globalInstall(cli, install) : dependencyInstall(cli, install);
8539
+ }
8540
+ //#endregion
8541
+ //#region ../agent-cli/dist/built-ins/doctor-rows.js
8542
+ function ok(message) {
8543
+ return {
8544
+ state: "ok",
8545
+ message
8546
+ };
8547
+ }
8548
+ function buildRow(cli) {
8549
+ return {
8550
+ name: "build",
8551
+ run: () => ok(versionLine(cli))
8552
+ };
8553
+ }
8554
+ function nodeRow(cli) {
8555
+ return {
8556
+ name: "node",
8557
+ run: () => {
8558
+ const found = process.versions.node;
8559
+ const range = cli.engines?.node;
8560
+ if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
8561
+ const minimum = engineMinimum(range);
8562
+ if (minimum === void 0) return {
8563
+ state: "problem",
8564
+ message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
8565
+ };
8566
+ if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
8567
+ const major = minimum[0];
8568
+ return {
8569
+ state: "problem",
8570
+ message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
8571
+ fix: `nvm install ${major} && nvm alias default ${major}`
8572
+ };
8573
+ }
8574
+ };
8575
+ }
8576
+ /** A row's thrown error as its problem: a `CliError` with its hint and code, anything else a bug. */
8577
+ function problemOfError(name, error) {
8578
+ if (error instanceof CliError) return {
8579
+ state: "problem",
8580
+ message: `${error.message}; ${error.hint}`,
8581
+ code: error.code
8582
+ };
8583
+ return {
8584
+ state: "problem",
8585
+ message: `the ${name} row hit a bug: ${messageOf(error)}`
8586
+ };
8587
+ }
8588
+ function readProject(projectFile, ctx) {
8589
+ if (projectFile === void 0) return {
8590
+ ok: true,
8591
+ project: void 0
8592
+ };
8593
+ try {
8594
+ return {
8595
+ ok: true,
8596
+ project: projectOf(projectFile, ctx.cwd, {
8597
+ fileError: ctx.fileError,
8598
+ callerLine: ctx.commandLine()
8599
+ })
8600
+ };
8601
+ } catch (error) {
8602
+ return {
8603
+ ok: false,
8604
+ error
8605
+ };
8606
+ }
8607
+ }
8608
+ function projectRow({ file }, read) {
8609
+ return {
8610
+ name: "project",
8611
+ run: (ctx) => {
8612
+ if (!read.ok) return problemOfError("project", read.error);
8613
+ return ok(read.project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(read.project.root, file));
8614
+ }
8615
+ };
8616
+ }
8617
+ function installRow(check) {
8618
+ return {
8619
+ name: "install",
8620
+ advisory: true,
8621
+ run: check.install
8622
+ };
8623
+ }
8624
+ function registryRow(cli, check) {
8625
+ return {
8626
+ name: "registry",
8627
+ advisory: true,
8628
+ run: async (ctx) => {
8629
+ const lookup = await check.release(ctx);
8630
+ if ("skipped" in lookup) return lookup.skipped;
8631
+ const { npm, advise } = lookup;
8632
+ const latest = await latestVersion(npm);
8633
+ const standing = standingOf$1(cli.version, latest);
8634
+ const head = `${npm.registry.label} has ${latest} as latest: this build`;
8635
+ if (standing === "behind") {
8636
+ const message = `${head} (${cli.version}) is older`;
8637
+ const advice = advise(latest);
8638
+ return "fix" in advice ? {
8639
+ state: "problem",
8640
+ message,
8641
+ fix: advice.fix
8642
+ } : {
8643
+ state: "problem",
8644
+ message: `${message}; ${advice.note}`
8645
+ };
8646
+ }
8647
+ return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
8648
+ }
8649
+ };
8650
+ }
8651
+ /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
8652
+ function doctorChecks(cli, launch, read) {
8653
+ const install = installOf(cli);
8654
+ const check = install.check(launch);
8655
+ return [
8656
+ buildRow(cli),
8657
+ nodeRow(cli),
8658
+ ...cli.project === void 0 ? [] : [projectRow(cli.project, read)],
8659
+ ...cli.doctor ?? [],
8660
+ ...toolRows(cli.tools ?? [], cli.commands),
8661
+ installRow(check),
8662
+ registryRow(cli, check),
8663
+ ...skillOf(cli, install).rows
8664
+ ];
8665
+ }
8666
+ //#endregion
8667
+ //#region ../agent-cli/dist/built-ins/doctor.js
8668
+ const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
8669
+ const DEFAULT_DEADLINE_MS = 1e4;
8670
+ const MS_PER_SECOND = 1e3;
8671
+ const MIN_LABEL_WIDTH = 8;
8672
+ const reportSchema = object({
8673
+ rows: array(object({
8674
+ check: string(),
8675
+ state: _enum([
8676
+ "ok",
8677
+ "problem",
8678
+ "skipped"
8679
+ ]),
8680
+ message: string(),
8681
+ fix: string().optional(),
8682
+ code: string().optional(),
8683
+ skill: packagedSkillDetailsSchema.optional()
8684
+ })),
8685
+ next: array(string())
8686
+ });
8687
+ function problemOf(check, error, { signal, deadline, ms }) {
8688
+ if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
8689
+ state: "problem",
8690
+ message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
8691
+ };
8692
+ return problemOfError(check.name, error);
8693
+ }
8694
+ function rowContext(ctx, signal, read) {
8695
+ return Object.create(ctx, {
8696
+ signal: { value: signal },
8697
+ project: { get: () => {
8698
+ if (!read.ok) throw read.error;
8699
+ return read.project;
8700
+ } }
8701
+ });
8702
+ }
8703
+ async function rowOf(check, ctx, read) {
8704
+ const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
8705
+ const signal = ctx.deadline(ms);
8706
+ let finding;
8707
+ try {
8708
+ finding = await Promise.race([Promise.resolve().then(() => check.run(rowContext(ctx, signal, read))), untilAborted(signal)]);
8709
+ } catch (error) {
8710
+ if (ctx.signal.aborted) return void 0;
8711
+ finding = !read.ok && error === read.error ? {
8712
+ state: "skipped",
8713
+ message: "the Project file is unusable; the project row says why"
8714
+ } : problemOf(check, error, {
8715
+ signal: ctx.signal,
8716
+ deadline: signal,
8717
+ ms
8718
+ });
8719
+ }
8720
+ return {
8721
+ row: {
8722
+ check: check.name,
8723
+ ...finding
8724
+ },
8725
+ advisory: check.advisory === true
8726
+ };
8727
+ }
8728
+ function nextOf(rows) {
8729
+ return [...new Set(rows.flatMap(({ fix }) => fix === void 0 ? [] : [fix]))];
8730
+ }
8731
+ function render({ rows, next }, style) {
8732
+ const width = Math.max(MIN_LABEL_WIDTH, ...rows.map(({ check }) => check.length)) + 2;
8733
+ const stateWidth = Math.max(...rows.map(({ state }) => state.length)) + 2;
8734
+ return [...rows.map(({ check, state, message }) => {
8735
+ const label = state === "problem" ? style.red(state) : state;
8736
+ return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
8737
+ }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
8738
+ }
8739
+ function failure(cliName, callerLine, problems) {
8740
+ const errors = problems.map(({ check, message, fix, code }) => new CliError({
8741
+ code: code ?? "DOCTOR_PROBLEMS",
8742
+ message: `${cliName} doctor found a problem in ${check}: ${message}`,
8743
+ hint: fix === void 0 ? "the row says what is wrong" : `run \`${fix}\`, then run \`${callerLine}\` again`
8744
+ }));
8745
+ return new AggregateError(errors);
8746
+ }
8747
+ /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
8748
+ function doctorCommand(cli, launch) {
8749
+ return defineCommand$1({
8750
+ name: "doctor",
8751
+ description: "check the setup and say what fixes each problem",
8752
+ examples: [{
8753
+ command: `${runAsOf(cli)} doctor`,
8754
+ note: "check the setup and list the commands that fix it"
8755
+ }, {
8756
+ command: `${runAsOf(cli)} doctor --json`,
8757
+ note: "get the rows and fixes as data"
8758
+ }],
8759
+ effect: "read",
8760
+ output: reportSchema,
8761
+ run: async (_input, ctx) => {
8762
+ const read = readProject(cli.project, ctx);
8763
+ const checked = (await Promise.all(doctorChecks(cli, launch, read).map((check) => rowOf(check, ctx, read)))).filter((row) => row !== void 0);
8764
+ const rows = checked.map(({ row }) => row);
8765
+ const report = {
8766
+ rows,
8767
+ next: nextOf(rows)
8768
+ };
8769
+ if (ctx.signal.aborted) {
8770
+ ctx.deliver(report);
8771
+ ctx.signal.throwIfAborted();
8772
+ }
8773
+ const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
8774
+ if (problems.length > 0) {
8775
+ ctx.deliver(report);
8776
+ throw failure(cli.name, ctx.commandLine(), problems);
8777
+ }
8778
+ return report;
8779
+ },
8780
+ render
8781
+ });
8782
+ }
8783
+ //#endregion
8748
8784
  //#region ../agent-cli/dist/built-ins/commands.js
8749
8785
  /**
8750
8786
  * The Commands the Runtime adds to a multi-Command CLI: `update` only for a Global install, and
8751
8787
  * `skill install` only for a Packaged skill.
8752
8788
  */
8753
8789
  function builtInCommands(cli, launch) {
8754
- const packaged = packagedFolderOf(cli);
8790
+ const install = installOf(cli);
8755
8791
  return {
8756
8792
  doctor: doctorCommand(cli, launch),
8757
- ...cli.install?.mode === "global" ? { update: updateCommand(cli, cli.install, launch) } : {},
8758
- ...packaged === void 0 ? {} : { skill: { install: skillInstallCommand(cli, packaged) } }
8793
+ ...install.builtIns(launch),
8794
+ ...skillOf(cli, install).builtIns
8759
8795
  };
8760
8796
  }
8761
8797
  //#endregion
@@ -8879,7 +8915,7 @@ function reachableExitClasses(cli) {
8879
8915
  "caller",
8880
8916
  "operator",
8881
8917
  "wait",
8882
- ...cli.commands !== void 0 && cli.install !== void 0 ? ["source"] : []
8918
+ ...cli.commands === void 0 ? [] : installOf(cli).exitClasses
8883
8919
  ];
8884
8920
  const own = Object.values(cli.errors ?? {}).map((declaration) => declaration.class);
8885
8921
  return /* @__PURE__ */ new Set([...runtime, ...own]);
@@ -8905,7 +8941,7 @@ function checkOwnCodes(cli) {
8905
8941
  function defineCli$1(cli) {
8906
8942
  checkOwnCodes(cli);
8907
8943
  if (cli.commands === void 0) {
8908
- if (packagedFolderOf(cli) !== void 0) throw new Error(`${cli.name} declares a Packaged skill, which only a CLI with several Commands can install`);
8944
+ if (skillOf(cli, installOf(cli)).kind === "packaged") throw new Error(`${cli.name} declares a Packaged skill, which only a CLI with several Commands can install`);
8909
8945
  return cli;
8910
8946
  }
8911
8947
  const { commands } = cli;
@@ -10023,7 +10059,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10023
10059
  let served = false;
10024
10060
  let settled;
10025
10061
  try {
10026
- const outcome = await control.settle(noticeSkillCopy(cli, {
10062
+ const outcome = await control.settle(skillOf(cli, installOf(cli)).notice({
10027
10063
  builtIn,
10028
10064
  bin: options.bin
10029
10065
  }, ctx).then(() => execute(command, input, ctx, {
@@ -10222,14 +10258,14 @@ async function runProcess(cli) {
10222
10258
  }
10223
10259
  //#endregion
10224
10260
  //#region package.json
10225
- var version$1 = "0.15.0";
10261
+ var version$1 = "0.15.1";
10226
10262
  var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
10227
10263
  //#endregion
10228
10264
  //#region src/build.ts
10229
10265
  const build = {
10230
- "sha": "cd3df50",
10266
+ "sha": "da48f36",
10231
10267
  "dirty": false,
10232
- "builtAt": "2026-10-27T04:23:15.000Z"
10268
+ "builtAt": "2026-10-27T08:31:19.000Z"
10233
10269
  };
10234
10270
  //#endregion
10235
10271
  //#region src/fill.ts