@duyquangnvx/create-agent-cli 0.15.0 → 0.16.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/bin.js +756 -725
  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,36 @@ 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
+ check: (launch) => checkOf$1(cli, install, runAs, launch)
7525
7394
  };
7526
7395
  }
7527
7396
  //#endregion
7528
- //#region ../agent-cli/dist/built-ins/skills.js
7397
+ //#region ../agent-cli/dist/built-ins/skill/hub.js
7529
7398
  const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7530
7399
  const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
7531
7400
  const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
@@ -7583,479 +7452,168 @@ function fixFor(cliName, copies, source) {
7583
7452
  if (has("unversioned") && add !== void 0) return has("behind") ? `${update} && ${add}` : add;
7584
7453
  return has("behind") || has("unversioned") ? update : void 0;
7585
7454
  }
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
- };
7455
+ const SKILL_STATE_NOTES = {
7456
+ current: "the same as this build",
7457
+ behind: "older than this build",
7458
+ ahead: "newer than this build",
7459
+ unversioned: "states no metadata.version",
7460
+ unreadable: "could not be read"
7461
+ };
7462
+ function describeCopy({ path, version, state }) {
7463
+ return `${path}${version === void 0 ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
7593
7464
  }
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;
7599
- }
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);
7465
+ /** A skill that `npx skills add` installs from `source`, the Skill Hub; with no `source`, no line adds it. */
7466
+ function hubSkill(cli, source) {
7467
+ const findingAt = async (version, ctx) => {
7468
+ const copies = await skillCopies(cli.name, version, ctx);
7469
+ const fix = fixFor(cli.name, copies, source);
7470
+ const message = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
7471
+ return fix === void 0 ? {
7472
+ state: "ok",
7473
+ message
7474
+ } : {
7475
+ state: "ok",
7476
+ message,
7477
+ fix
7478
+ };
7479
+ };
7603
7480
  return {
7604
- root,
7605
- copy: join(root, ".claude", "skills", cli.name)
7481
+ kind: "hub",
7482
+ builtIns: {},
7483
+ rows: [{
7484
+ name: "skill",
7485
+ advisory: true,
7486
+ run: (ctx) => findingAt(cli.version, ctx)
7487
+ }],
7488
+ notice: async () => {},
7489
+ findingAt
7606
7490
  };
7607
7491
  }
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();
7614
- }
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;
7492
+ //#endregion
7493
+ //#region ../agent-cli/dist/built-ins/agents-md.js
7494
+ function markersOf(name) {
7620
7495
  return {
7621
- packageVersion,
7622
- copyVersion,
7623
- same: copyVersion === packageVersion
7496
+ begin: `<!-- BEGIN:${name} -->`,
7497
+ end: `<!-- END:${name} -->`
7624
7498
  };
7625
7499
  }
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`;
7500
+ /** `text` with the CLI's block between its markers, appended when they are missing; nothing else changes. */
7501
+ function withBlock({ name, text: block }, text) {
7502
+ const { begin, end } = markersOf(name);
7503
+ const whole = `${begin}\n${block}\n${end}`;
7504
+ const start = text.indexOf(begin);
7505
+ const stop = start === -1 ? -1 : text.indexOf(end, start);
7506
+ if (stop !== -1) return `${text.slice(0, start)}${whole}${text.slice(stop + end.length)}`;
7507
+ if (text === "") return `${whole}\n`;
7508
+ return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
7645
7509
  }
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
7510
+ async function agentsMdAt(agents, root) {
7511
+ const path = join(root, "AGENTS.md");
7512
+ const existing = await orMissing(readFile(path, "utf8"));
7513
+ if (existing === void 0) return {
7514
+ path,
7515
+ agents,
7516
+ change: "create"
7675
7517
  };
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
7518
+ if (withBlock(agents, existing) === existing) return {
7519
+ path,
7520
+ agents,
7521
+ change: null
7699
7522
  };
7700
- }
7701
- function noInstall(cli) {
7702
7523
  return {
7703
- state: "skipped",
7704
- message: `not checked: ${cli.name} declares no install`
7524
+ path,
7525
+ agents,
7526
+ change: existing.includes(markersOf(agents.name).begin) ? "rewrite" : "add"
7705
7527
  };
7706
7528
  }
7707
- function buildRow(cli) {
7708
- return {
7709
- name: "build",
7710
- run: () => ok(versionLine(cli))
7711
- };
7529
+ async function handEditsAt(root) {
7530
+ const claudeMd = join(root, "CLAUDE.md");
7531
+ const text = await orMissing(readFile(claudeMd, "utf8"));
7532
+ if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
7533
+ return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
7712
7534
  }
7713
- function nodeRow(cli) {
7535
+ function agentsRow(agents, install) {
7536
+ const { name } = agents;
7714
7537
  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 {
7538
+ name: "agents",
7539
+ advisory: true,
7540
+ run: async (ctx) => {
7541
+ const root = await projectRootOf(ctx.cwd);
7542
+ const { path, change } = await agentsMdAt(agents, root);
7543
+ const fix = `${install.runAs} skill install`;
7544
+ if (change === "rewrite") return {
7722
7545
  state: "problem",
7723
- message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
7546
+ message: `${path} holds a ${name} block other than the one ${fix} writes`,
7547
+ fix
7724
7548
  };
7725
- if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
7726
- const major = minimum[0];
7727
- return {
7549
+ if (change !== null) return {
7728
7550
  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}`
7551
+ message: `${path} holds no ${name} block, so sessions in this repo do not know ${name}`,
7552
+ fix
7553
+ };
7554
+ const holds = `${path} holds the ${name} block`;
7555
+ const [edit] = await handEditsAt(root);
7556
+ return edit === void 0 ? {
7557
+ state: "ok",
7558
+ message: holds
7559
+ } : {
7560
+ state: "problem",
7561
+ message: `${holds}; ${edit}`
7731
7562
  };
7732
7563
  }
7733
7564
  };
7734
7565
  }
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
- }
7566
+ //#endregion
7567
+ //#region ../agent-cli/dist/agent.js
7568
+ const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
7569
+ const isSet = (name) => (env) => (env[name] ?? "") !== "";
7570
+ const equals = (name, value) => (env) => env[name] === value;
7571
+ const MARKERS = [
7572
+ (env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
7573
+ equals("AGENT", "amp"),
7574
+ isSet("CODEX_SANDBOX"),
7575
+ isSet("CODEX_CI"),
7576
+ isSet("CODEX_THREAD_ID"),
7577
+ isSet("GEMINI_CLI"),
7578
+ isSet("COPILOT_CLI"),
7579
+ isSet("OPENCODE"),
7580
+ isSet("ANTIGRAVITY_AGENT"),
7581
+ isSet("AUGMENT_AGENT"),
7582
+ isSet("REPL_ID"),
7583
+ isSet("CLAUDE_CODE_IS_COWORK"),
7584
+ isSet("CLAUDECODE"),
7585
+ isSet("CLAUDE_CODE"),
7586
+ isSet("CURSOR_TRACE_ID"),
7587
+ isSet("CURSOR_AGENT"),
7588
+ equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
7589
+ equals("TERM_PROGRAM", "kiro"),
7590
+ (env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
7591
+ isSet("GOOSE_PROVIDER")
7592
+ ];
7593
+ /**
7594
+ * Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
7595
+ * in a PTY, so a TTY alone doesn't mean a person is watching.
7596
+ */
7597
+ function isAgent(env) {
7598
+ return MARKERS.some((marker) => marker(env));
7766
7599
  }
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
- };
7600
+ //#endregion
7601
+ //#region ../agent-cli/dist/errno.js
7602
+ const PERMISSION_DENIED = "PERMISSION_DENIED";
7603
+ const DISK_FULL = "DISK_FULL";
7604
+ const NOT_FOUND = "NOT_FOUND";
7605
+ const ALREADY_EXISTS = "ALREADY_EXISTS";
7606
+ const NOT_A_FILE = "NOT_A_FILE";
7607
+ function permissionHint({ path, access, callerLine, env, platform }) {
7608
+ if (platform === "win32") return `a program may hold ${path} open: close it, then run \`${callerLine}\` again`;
7609
+ if (!isAgent(env)) return `check the permissions of ${path} and its folder`;
7610
+ return `your sandbox may block it: ask your user to run \`! ${callerLine}\`, or ${access === "read" ? `to allow reading ${path} in the sandbox` : `to allow ${path} in the sandbox's write list`}`;
7775
7611
  }
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`}`;
8053
- }
8054
- function fileErrorOf(scope) {
8055
- return (error, at) => classifyErrno(error, {
8056
- ...at,
8057
- ...scope
8058
- });
7612
+ function fileErrorOf(scope) {
7613
+ return (error, at) => classifyErrno(error, {
7614
+ ...at,
7615
+ ...scope
7616
+ });
8059
7617
  }
8060
7618
  function classifyErrno(error, context) {
8061
7619
  const { path } = context;
@@ -8337,26 +7895,52 @@ async function probeOutputs({ files, cwd, fileError }, outputs) {
8337
7895
  }));
8338
7896
  }
8339
7897
  //#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)]);
8353
- return a.equals(b);
8354
- }
8355
- async function planOf(cli, folder, cwd, packageRoot) {
8356
- const source = join(packageRoot, folder);
8357
- const { root, copy } = await skillCopyOf(cli, cwd);
8358
- const [shipped, present] = await Promise.all([filesIn(source), filesIn(copy)]);
8359
- const differs = await Promise.all(shipped.map(async (file) => present.includes(file) && !await sameContent(join(source, file), join(copy, file))));
7898
+ //#region ../agent-cli/dist/built-ins/skill/install-command.js
7899
+ /** The project root and the Skill copy under it, as `skill install` writes it. */
7900
+ async function skillCopyOf(cli, cwd) {
7901
+ const root = await projectRootOf(cwd);
7902
+ return {
7903
+ root,
7904
+ copy: join(root, ".claude", "skills", cli.name)
7905
+ };
7906
+ }
7907
+ /** Every file under `dir`, relative to it and sorted; none when `dir` is missing. */
7908
+ async function filesIn(dir) {
7909
+ return (await orMissing(readdir(dir, {
7910
+ recursive: true,
7911
+ withFileTypes: true
7912
+ })) ?? []).filter((entry) => entry.isFile()).map((entry) => relative(dir, join(entry.parentPath, entry.name))).sort();
7913
+ }
7914
+ /** The versions of the package's skill in `source` and of its copy in `copy`. */
7915
+ async function skillVersionsOf(source, copy) {
7916
+ const [shipped, installed] = await Promise.all([readFile(join(source, "SKILL.md"), "utf8"), orMissing(readFile(join(copy, "SKILL.md"), "utf8"))]);
7917
+ const packageVersion = skillVersionIn(shipped) ?? null;
7918
+ const copyVersion = installed === void 0 ? void 0 : skillVersionIn(installed) ?? null;
7919
+ return {
7920
+ packageVersion,
7921
+ copyVersion,
7922
+ same: copyVersion === packageVersion
7923
+ };
7924
+ }
7925
+ const installSchema = object({
7926
+ copy: string(),
7927
+ written: array(string()),
7928
+ removed: array(string()),
7929
+ agentsMd: object({
7930
+ path: string(),
7931
+ changed: boolean()
7932
+ }).nullable(),
7933
+ handEdits: array(string())
7934
+ });
7935
+ async function sameContent(left, right) {
7936
+ const [a, b] = await Promise.all([readFile(left), readFile(right)]);
7937
+ return a.equals(b);
7938
+ }
7939
+ async function planOf(cli, folder, agents, cwd, packageRoot) {
7940
+ const source = join(packageRoot, folder);
7941
+ const { root, copy } = await skillCopyOf(cli, cwd);
7942
+ const [shipped, present] = await Promise.all([filesIn(source), filesIn(copy)]);
7943
+ const differs = await Promise.all(shipped.map(async (file) => present.includes(file) && !await sameContent(join(source, file), join(copy, file))));
8360
7944
  return {
8361
7945
  source,
8362
7946
  copy,
@@ -8364,8 +7948,8 @@ async function planOf(cli, folder, cwd, packageRoot) {
8364
7948
  replace: shipped.filter((_file, index) => differs[index]),
8365
7949
  remove: present.filter((file) => !shipped.includes(file)),
8366
7950
  sameVersion: (await skillVersionsOf(source, copy)).same,
8367
- agentsMd: await agentsMdAt(cli, root),
8368
- handEdits: await handEditsAt(root)
7951
+ agentsMd: agents === void 0 ? null : await agentsMdAt(agents, root),
7952
+ handEdits: agents === void 0 ? [] : await handEditsAt(root)
8369
7953
  };
8370
7954
  }
8371
7955
  const BLOCK_VERBS = {
@@ -8379,11 +7963,11 @@ function renderPlan(cli, plan) {
8379
7963
  ...plan.replace.map((file) => ` replace ${file}`),
8380
7964
  ...plan.remove.map((file) => ` remove ${file}`)
8381
7965
  ];
8382
- const { path, block, change } = plan.agentsMd;
7966
+ const { agentsMd } = plan;
8383
7967
  return [
8384
7968
  files.length === 0 ? `the ${cli.name} skill at ${plan.copy} already matches the package` : `would copy the ${cli.name} skill to ${plan.copy}:`,
8385
7969
  ...files,
8386
- change === null ? `${path} already holds the ${cli.name} block` : `would ${BLOCK_VERBS[change]} ${path}:\n${block}`,
7970
+ ...agentsMd === null ? [] : [agentsMd.change === null ? `${agentsMd.path} already holds the ${cli.name} block` : `would ${BLOCK_VERBS[agentsMd.change]} ${agentsMd.path}:\n${agentsMd.agents.text}`],
8387
7971
  ...plan.handEdits.map((edit) => `by hand: ${edit}`)
8388
7972
  ].join("\n");
8389
7973
  }
@@ -8391,7 +7975,7 @@ function renderInstalled(cli, data) {
8391
7975
  const { written, removed, agentsMd } = data;
8392
7976
  return [
8393
7977
  written.length + removed.length === 0 ? `the ${cli.name} skill at ${data.copy} already matches the package` : `copied the ${cli.name} skill to ${data.copy}: wrote ${written.length} files, removed ${removed.length}`,
8394
- agentsMd.changed ? `wrote the ${cli.name} block in ${agentsMd.path}` : `${agentsMd.path} already holds the ${cli.name} block`,
7978
+ ...agentsMd === null ? [] : [agentsMd.changed ? `wrote the ${cli.name} block in ${agentsMd.path}` : `${agentsMd.path} already holds the ${cli.name} block`],
8395
7979
  ...data.handEdits.map((edit) => `by hand: ${edit}`)
8396
7980
  ].join("\n");
8397
7981
  }
@@ -8404,11 +7988,11 @@ function editedRemovals(plan, ctx) {
8404
7988
  });
8405
7989
  }
8406
7990
  /** The built-in `skill install`, for a CLI whose skill ships in its package. */
8407
- function skillInstallCommand(cli, folder) {
8408
- const runAs = runAsOf(cli);
7991
+ function skillInstallCommand(cli, folder, install, agents) {
7992
+ const { runAs } = install;
8409
7993
  return defineCommand$1({
8410
7994
  name: "install",
8411
- description: "copy this CLI's skill into the project and write its AGENTS.md block",
7995
+ description: agents === void 0 ? "copy this CLI's skill into the project" : "copy this CLI's skill into the project and write its AGENTS.md block",
8412
7996
  examples: [{
8413
7997
  command: `${runAs} skill install`,
8414
7998
  note: "install the skill this version ships"
@@ -8419,24 +8003,24 @@ function skillInstallCommand(cli, folder) {
8419
8003
  effect: "write",
8420
8004
  output: installSchema,
8421
8005
  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),
8006
+ plan: (_input, ctx) => planOf(cli, folder, agents, ctx.cwd, ctx.packageRoot),
8423
8007
  outputs: (plan) => plan.sameVersion ? [...plan.write, ...plan.replace].map((file) => join(plan.copy, file)) : [],
8424
8008
  apply: async (plan, ctx) => {
8425
8009
  if (plan.sameVersion && plan.remove.length > 0 && !ctx.force) throw editedRemovals(plan, ctx);
8426
8010
  for (const file of [...plan.write, ...plan.replace]) await ctx.files.write(join(plan.copy, file), await readFile(join(plan.source, file)));
8427
8011
  for (const file of plan.remove) await ctx.files.remove(join(plan.copy, file));
8428
- const { path, change } = plan.agentsMd;
8429
- if (change !== null) {
8430
- const existing = await orMissing(readFile(path, "utf8")) ?? "";
8431
- await ctx.files.write(path, withBlock(cli, existing));
8012
+ const { agentsMd } = plan;
8013
+ if (agentsMd !== null && agentsMd.change !== null) {
8014
+ const existing = await orMissing(readFile(agentsMd.path, "utf8")) ?? "";
8015
+ await ctx.files.write(agentsMd.path, withBlock(agentsMd.agents, existing));
8432
8016
  }
8433
8017
  return {
8434
8018
  copy: plan.copy,
8435
8019
  written: [...plan.write, ...plan.replace].sort(),
8436
8020
  removed: [...plan.remove],
8437
- agentsMd: {
8438
- path,
8439
- changed: change !== null
8021
+ agentsMd: agentsMd === null ? null : {
8022
+ path: agentsMd.path,
8023
+ changed: agentsMd.change !== null
8440
8024
  },
8441
8025
  handEdits: [...plan.handEdits]
8442
8026
  };
@@ -8446,6 +8030,97 @@ function skillInstallCommand(cli, folder) {
8446
8030
  });
8447
8031
  }
8448
8032
  //#endregion
8033
+ //#region ../agent-cli/dist/built-ins/skill/packaged.js
8034
+ async function personalCopyOf(name, copy, ctx) {
8035
+ const home = join(homeOf(ctx.env), ".claude", "skills", name);
8036
+ const real = await orMissing(realpath(home));
8037
+ return real === void 0 || real === await orMissing(realpath(copy)) ? void 0 : home;
8038
+ }
8039
+ /** What the `skill` row of a Packaged skill carries under `--json` beside its message. */
8040
+ const packagedSkillDetailsSchema = object({
8041
+ copy: string(),
8042
+ copyVersion: string().nullable(),
8043
+ packageVersion: string().nullable(),
8044
+ /** A personal copy, which Claude Code prefers over the project's. */
8045
+ home: string().optional()
8046
+ });
8047
+ function versionLabel(version) {
8048
+ return version ?? "(no metadata.version)";
8049
+ }
8050
+ function compared(name, { copy, copyVersion, packageVersion }, same) {
8051
+ const stated = `${copy} ${versionLabel(copyVersion)}`;
8052
+ return same ? `${stated}, the version ${name} ships` : `${stated} differs from the ${versionLabel(packageVersion)} that ${name} ships`;
8053
+ }
8054
+ /** The project root's copy against the package's, and any under home. */
8055
+ async function findingOf(cli, folder, install, ctx) {
8056
+ const { copy } = await skillCopyOf(cli, ctx.cwd);
8057
+ const versions = await skillVersionsOf(join(ctx.packageRoot, folder), copy);
8058
+ const { packageVersion } = versions;
8059
+ const copyVersion = versions.copyVersion ?? null;
8060
+ const home = await personalCopyOf(cli.name, copy, ctx);
8061
+ const skill = {
8062
+ copy,
8063
+ copyVersion,
8064
+ packageVersion,
8065
+ ...home === void 0 ? {} : { home }
8066
+ };
8067
+ const head = versions.copyVersion === void 0 ? `no copy of the ${cli.name} skill at ${copy}` : compared(cli.name, skill, versions.same);
8068
+ const message = home === void 0 ? head : `${head}; remove ${home}: Claude Code prefers a personal skill over the project's`;
8069
+ if (!versions.same) return {
8070
+ state: "problem",
8071
+ message,
8072
+ fix: `${install.runAs} skill install`,
8073
+ skill
8074
+ };
8075
+ return home === void 0 ? {
8076
+ state: "ok",
8077
+ message,
8078
+ skill
8079
+ } : {
8080
+ state: "problem",
8081
+ message,
8082
+ skill
8083
+ };
8084
+ }
8085
+ async function notice(cli, folder, install, { builtIn, bin }, ctx) {
8086
+ if (builtIn || bin === void 0 || cli.commands === void 0) return;
8087
+ let finding;
8088
+ try {
8089
+ finding = await findingOf(cli, folder, install, ctx);
8090
+ } catch (error) {
8091
+ if (errnoOf(error) === void 0) throw error;
8092
+ ctx.notice(`could not check the ${cli.name} skill: ${messageOf(error)}`);
8093
+ return;
8094
+ }
8095
+ if (finding.state !== "problem") return;
8096
+ ctx.notice(finding.fix === void 0 ? finding.message : `${finding.message}; run \`${finding.fix}\``);
8097
+ }
8098
+ /** A skill shipped in the package at `packaged`, which `skill install` copies to the project root. */
8099
+ function packagedSkill(cli, { packaged: folder, agents: text }, install) {
8100
+ const agents = text === void 0 ? void 0 : {
8101
+ name: cli.name,
8102
+ text
8103
+ };
8104
+ return {
8105
+ kind: "packaged",
8106
+ builtIns: { skill: { install: skillInstallCommand(cli, folder, install, agents) } },
8107
+ rows: [{
8108
+ name: "skill",
8109
+ advisory: true,
8110
+ run: (ctx) => findingOf(cli, folder, install, ctx)
8111
+ }, ...agents === void 0 ? [] : [agentsRow(agents, install)]],
8112
+ notice: (launch, ctx) => notice(cli, folder, install, launch, ctx),
8113
+ findingAt: (_version, ctx) => findingOf(cli, folder, install, ctx)
8114
+ };
8115
+ }
8116
+ //#endregion
8117
+ //#region ../agent-cli/dist/built-ins/skill/skill.js
8118
+ /** A Packaged skill, or else a Skill Hub's, also for a CLI that declares no `skill`. */
8119
+ function skillOf(cli, install) {
8120
+ const { skill } = cli;
8121
+ return skill !== void 0 && "packaged" in skill ? packagedSkill(cli, skill, install) : hubSkill(cli, skill?.source);
8122
+ }
8123
+ //#endregion
8449
8124
  //#region ../agent-cli/dist/interrupts.js
8450
8125
  const INTERRUPTED = "INTERRUPTED";
8451
8126
  const DEADLINE_EXCEEDED = "DEADLINE_EXCEEDED";
@@ -8583,6 +8258,59 @@ var RunControl = class {
8583
8258
  }
8584
8259
  };
8585
8260
  //#endregion
8261
+ //#region ../agent-cli/dist/built-ins/install/global-npm.js
8262
+ const NOT_GLOBAL_INSTALL = "NOT_GLOBAL_INSTALL";
8263
+ /** Where npm puts global packages under its global prefix. */
8264
+ function globalModulesDir(prefix) {
8265
+ return process.platform === "win32" ? join(prefix, "node_modules") : join(prefix, "lib", "node_modules");
8266
+ }
8267
+ function notGlobal(ctx, message) {
8268
+ return new CliError({
8269
+ code: NOT_GLOBAL_INSTALL,
8270
+ message,
8271
+ hint: `install it with npm, so \`${ctx.cliName} update\` can replace it: ${installCommandLine(ctx.registry)}`
8272
+ });
8273
+ }
8274
+ /**
8275
+ * The global npm install this copy runs from, found by asking npm for its global prefix.
8276
+ * `bin` is the script that started the CLI; throws `NOT_GLOBAL_INSTALL` when it is not inside that install.
8277
+ */
8278
+ async function globalInstallOf(bin, ctx) {
8279
+ if (bin === void 0) throw notGlobal(ctx, `${ctx.cliName} runs from source, not from an npm install`);
8280
+ const prefix = await globalPrefix(ctx);
8281
+ const packageDir = join(globalModulesDir(prefix), ctx.registry.package);
8282
+ const installed = await orMissing(realpath(packageDir));
8283
+ const here = await orMissing(realpath(bin)) ?? bin;
8284
+ if (installed === void 0 || !isInside(installed, here)) throw notGlobal(ctx, `${ctx.cliName} runs from ${here}, not from npm's global install at ${packageDir}`);
8285
+ if ((await lstat(packageDir)).isSymbolicLink()) throw notGlobal(ctx, `${ctx.cliName} runs from a link to ${installed} (npm link), not from an npm install`);
8286
+ return {
8287
+ prefix,
8288
+ packageDir
8289
+ };
8290
+ }
8291
+ const NPMJS = "https://registry.npmjs.org/";
8292
+ /** The flag that points npm at the CLI's registry; none for npmjs, npm's default. */
8293
+ function registryFlags({ package: name, registry }) {
8294
+ if (registry === NPMJS) return [];
8295
+ const scope = scopeOf(name);
8296
+ return [scope === void 0 ? `--registry=${registry}` : `--${scope}:registry=${registry}`];
8297
+ }
8298
+ function globalRegistry(install) {
8299
+ const { package: name, registry } = install;
8300
+ return {
8301
+ package: name,
8302
+ label: registry,
8303
+ flags: registryFlags(install),
8304
+ unreachableHint: "check the network or VPN",
8305
+ loginHint: `log in to ${registry} (\`npm login --registry=${registry}\`) or set its token in ~/.npmrc`,
8306
+ missingHint: `check that ${name} is published on ${registry}`
8307
+ };
8308
+ }
8309
+ /** npm for a Global install, run in the working directory. */
8310
+ function globalNpm(cli, install, ctx, node) {
8311
+ return npmContext(cli, globalRegistry(install), ctx, node, ctx.cwd);
8312
+ }
8313
+ //#endregion
8586
8314
  //#region ../agent-cli/dist/built-ins/upgrades.js
8587
8315
  /** A skipped release's Upgrade steps, or its whole section when it lists none. */
8588
8316
  const upgradeSchema = union([object({
@@ -8675,10 +8403,9 @@ function renderUpdate(cli, install, update) {
8675
8403
  ...next
8676
8404
  ]).join("\n");
8677
8405
  }
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] : [];
8406
+ async function skillNext(skill, latest, ctx) {
8407
+ const { fix } = await skill.findingAt(latest, ctx);
8408
+ return fix === void 0 ? [] : [fix];
8682
8409
  }
8683
8410
  function interruptedAfterInstall(plan) {
8684
8411
  return new CliError({
@@ -8688,7 +8415,7 @@ function interruptedAfterInstall(plan) {
8688
8415
  });
8689
8416
  }
8690
8417
  /** The built-in `update`, for a CLI installed globally with npm. */
8691
- function updateCommand(cli, install, { bin, node }) {
8418
+ function updateCommand(cli, install, skill, { bin, node }) {
8692
8419
  return defineCommand$1({
8693
8420
  name: "update",
8694
8421
  description: "install the latest version of this CLI from its registry",
@@ -8719,7 +8446,7 @@ function updateCommand(cli, install, { bin, node }) {
8719
8446
  latest,
8720
8447
  installed: null,
8721
8448
  upgrades: [],
8722
- next: await skillNext(cli, latest, ctx)
8449
+ next: await skillNext(skill, latest, ctx)
8723
8450
  };
8724
8451
  ctx.signal.throwIfAborted();
8725
8452
  ctx.progress(`installing ${plan.package}@${latest} with the npm under ${prefix}…`);
@@ -8732,7 +8459,7 @@ function updateCommand(cli, install, { bin, node }) {
8732
8459
  prefix
8733
8460
  },
8734
8461
  upgrades: await upgradesOf(plan.packageDir, from, latest),
8735
- next: await skillNext(cli, latest, ctx)
8462
+ next: await skillNext(skill, latest, ctx)
8736
8463
  };
8737
8464
  if (ctx.signal.aborted) {
8738
8465
  ctx.deliver(update);
@@ -8745,17 +8472,321 @@ function updateCommand(cli, install, { bin, node }) {
8745
8472
  });
8746
8473
  }
8747
8474
  //#endregion
8475
+ //#region ../agent-cli/dist/built-ins/install/global.js
8476
+ function checkOf(cli, install, { bin, node }) {
8477
+ return {
8478
+ install: async (ctx) => {
8479
+ const npm = globalNpm(cli, install, ctx, node);
8480
+ try {
8481
+ return {
8482
+ state: "ok",
8483
+ message: `a global npm install under ${(await globalInstallOf(bin, npm)).prefix}`
8484
+ };
8485
+ } catch (error) {
8486
+ if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") return {
8487
+ state: "problem",
8488
+ message: error.message,
8489
+ fix: installCommandLine(npm.registry)
8490
+ };
8491
+ throw error;
8492
+ }
8493
+ },
8494
+ release: async (ctx) => {
8495
+ const fix = `${cli.name} update`;
8496
+ return {
8497
+ npm: globalNpm(cli, install, ctx, node),
8498
+ advise: () => ({ fix })
8499
+ };
8500
+ }
8501
+ };
8502
+ }
8503
+ /** The Install of a CLI installed once per machine with npm, which `update` replaces. */
8504
+ function globalInstall(cli, install) {
8505
+ const adapter = {
8506
+ runAs: runAsOf(cli),
8507
+ builtIns: (launch) => ({ update: updateCommand(cli, install, skillOf(cli, adapter), launch) }),
8508
+ exitClasses: ["source"],
8509
+ check: (launch) => checkOf(cli, install, launch)
8510
+ };
8511
+ return adapter;
8512
+ }
8513
+ //#endregion
8514
+ //#region ../agent-cli/dist/built-ins/install/install.js
8515
+ function noInstall(cli) {
8516
+ const skipped = async () => ({
8517
+ state: "skipped",
8518
+ message: `not checked: ${cli.name} declares no install`
8519
+ });
8520
+ return {
8521
+ runAs: runAsOf(cli),
8522
+ builtIns: () => ({}),
8523
+ exitClasses: [],
8524
+ check: () => ({
8525
+ install: skipped,
8526
+ release: async () => ({ skipped: await skipped() })
8527
+ })
8528
+ };
8529
+ }
8530
+ function installOf(cli) {
8531
+ const { install } = cli;
8532
+ if (install === void 0) return noInstall(cli);
8533
+ return install.mode === "global" ? globalInstall(cli, install) : dependencyInstall(cli, install);
8534
+ }
8535
+ //#endregion
8536
+ //#region ../agent-cli/dist/built-ins/doctor-rows.js
8537
+ function ok(message) {
8538
+ return {
8539
+ state: "ok",
8540
+ message
8541
+ };
8542
+ }
8543
+ function buildRow(cli) {
8544
+ return {
8545
+ name: "build",
8546
+ run: () => ok(versionLine(cli))
8547
+ };
8548
+ }
8549
+ function nodeRow(cli) {
8550
+ return {
8551
+ name: "node",
8552
+ run: () => {
8553
+ const found = process.versions.node;
8554
+ const range = cli.engines?.node;
8555
+ if (range === void 0) return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
8556
+ const minimum = engineMinimum(range);
8557
+ if (minimum === void 0) return {
8558
+ state: "problem",
8559
+ message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`
8560
+ };
8561
+ if (meetsMinimum(found, minimum)) return ok(`Node ${found} meets ${range}`);
8562
+ const major = minimum[0];
8563
+ return {
8564
+ state: "problem",
8565
+ message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
8566
+ fix: `nvm install ${major} && nvm alias default ${major}`
8567
+ };
8568
+ }
8569
+ };
8570
+ }
8571
+ /** A row's thrown error as its problem: a `CliError` with its hint and code, anything else a bug. */
8572
+ function problemOfError(name, error) {
8573
+ if (error instanceof CliError) return {
8574
+ state: "problem",
8575
+ message: `${error.message}; ${error.hint}`,
8576
+ code: error.code
8577
+ };
8578
+ return {
8579
+ state: "problem",
8580
+ message: `the ${name} row hit a bug: ${messageOf(error)}`
8581
+ };
8582
+ }
8583
+ function readProject(projectFile, ctx) {
8584
+ if (projectFile === void 0) return {
8585
+ ok: true,
8586
+ project: void 0
8587
+ };
8588
+ try {
8589
+ return {
8590
+ ok: true,
8591
+ project: projectOf(projectFile, ctx.cwd, {
8592
+ fileError: ctx.fileError,
8593
+ callerLine: ctx.commandLine()
8594
+ })
8595
+ };
8596
+ } catch (error) {
8597
+ return {
8598
+ ok: false,
8599
+ error
8600
+ };
8601
+ }
8602
+ }
8603
+ function projectRow({ file }, read) {
8604
+ return {
8605
+ name: "project",
8606
+ run: (ctx) => {
8607
+ if (!read.ok) return problemOfError("project", read.error);
8608
+ return ok(read.project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(read.project.root, file));
8609
+ }
8610
+ };
8611
+ }
8612
+ function installRow(check) {
8613
+ return {
8614
+ name: "install",
8615
+ advisory: true,
8616
+ run: check.install
8617
+ };
8618
+ }
8619
+ function registryRow(cli, check) {
8620
+ return {
8621
+ name: "registry",
8622
+ advisory: true,
8623
+ run: async (ctx) => {
8624
+ const lookup = await check.release(ctx);
8625
+ if ("skipped" in lookup) return lookup.skipped;
8626
+ const { npm, advise } = lookup;
8627
+ const latest = await latestVersion(npm);
8628
+ const standing = standingOf$1(cli.version, latest);
8629
+ const head = `${npm.registry.label} has ${latest} as latest: this build`;
8630
+ if (standing === "behind") {
8631
+ const message = `${head} (${cli.version}) is older`;
8632
+ const advice = advise(latest);
8633
+ return "fix" in advice ? {
8634
+ state: "problem",
8635
+ message,
8636
+ fix: advice.fix
8637
+ } : {
8638
+ state: "problem",
8639
+ message: `${message}; ${advice.note}`
8640
+ };
8641
+ }
8642
+ return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
8643
+ }
8644
+ };
8645
+ }
8646
+ /** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
8647
+ function doctorChecks(cli, launch, read) {
8648
+ const install = installOf(cli);
8649
+ const check = install.check(launch);
8650
+ return [
8651
+ buildRow(cli),
8652
+ nodeRow(cli),
8653
+ ...cli.project === void 0 ? [] : [projectRow(cli.project, read)],
8654
+ ...cli.doctor ?? [],
8655
+ ...toolRows(cli.tools ?? [], cli.commands),
8656
+ installRow(check),
8657
+ registryRow(cli, check),
8658
+ ...skillOf(cli, install).rows
8659
+ ];
8660
+ }
8661
+ //#endregion
8662
+ //#region ../agent-cli/dist/built-ins/doctor.js
8663
+ const DOCTOR_PROBLEMS = "DOCTOR_PROBLEMS";
8664
+ const DEFAULT_DEADLINE_MS = 1e4;
8665
+ const MS_PER_SECOND = 1e3;
8666
+ const MIN_LABEL_WIDTH = 8;
8667
+ const reportSchema = object({
8668
+ rows: array(object({
8669
+ check: string(),
8670
+ state: _enum([
8671
+ "ok",
8672
+ "problem",
8673
+ "skipped"
8674
+ ]),
8675
+ message: string(),
8676
+ fix: string().optional(),
8677
+ code: string().optional(),
8678
+ skill: packagedSkillDetailsSchema.optional()
8679
+ })),
8680
+ next: array(string())
8681
+ });
8682
+ function problemOf(check, error, { signal, deadline, ms }) {
8683
+ if (!signal.aborted && deadline.aborted && error === deadline.reason) return {
8684
+ state: "problem",
8685
+ message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
8686
+ };
8687
+ return problemOfError(check.name, error);
8688
+ }
8689
+ function rowContext(ctx, signal, read) {
8690
+ return Object.create(ctx, {
8691
+ signal: { value: signal },
8692
+ project: { get: () => {
8693
+ if (!read.ok) throw read.error;
8694
+ return read.project;
8695
+ } }
8696
+ });
8697
+ }
8698
+ async function rowOf(check, ctx, read) {
8699
+ const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
8700
+ const signal = ctx.deadline(ms);
8701
+ let finding;
8702
+ try {
8703
+ finding = await Promise.race([Promise.resolve().then(() => check.run(rowContext(ctx, signal, read))), untilAborted(signal)]);
8704
+ } catch (error) {
8705
+ if (ctx.signal.aborted) return void 0;
8706
+ finding = !read.ok && error === read.error ? {
8707
+ state: "skipped",
8708
+ message: "the Project file is unusable; the project row says why"
8709
+ } : problemOf(check, error, {
8710
+ signal: ctx.signal,
8711
+ deadline: signal,
8712
+ ms
8713
+ });
8714
+ }
8715
+ return {
8716
+ row: {
8717
+ check: check.name,
8718
+ ...finding
8719
+ },
8720
+ advisory: check.advisory === true
8721
+ };
8722
+ }
8723
+ function nextOf(rows) {
8724
+ return [...new Set(rows.flatMap(({ fix }) => fix === void 0 ? [] : [fix]))];
8725
+ }
8726
+ function render({ rows, next }, style) {
8727
+ const width = Math.max(MIN_LABEL_WIDTH, ...rows.map(({ check }) => check.length)) + 2;
8728
+ const stateWidth = Math.max(...rows.map(({ state }) => state.length)) + 2;
8729
+ return [...rows.map(({ check, state, message }) => {
8730
+ const label = state === "problem" ? style.red(state) : state;
8731
+ return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
8732
+ }), ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
8733
+ }
8734
+ function failure(cliName, callerLine, problems) {
8735
+ const errors = problems.map(({ check, message, fix, code }) => new CliError({
8736
+ code: code ?? "DOCTOR_PROBLEMS",
8737
+ message: `${cliName} doctor found a problem in ${check}: ${message}`,
8738
+ hint: fix === void 0 ? "the row says what is wrong" : `run \`${fix}\`, then run \`${callerLine}\` again`
8739
+ }));
8740
+ return new AggregateError(errors);
8741
+ }
8742
+ /** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
8743
+ function doctorCommand(cli, launch) {
8744
+ return defineCommand$1({
8745
+ name: "doctor",
8746
+ description: "check the setup and say what fixes each problem",
8747
+ examples: [{
8748
+ command: `${runAsOf(cli)} doctor`,
8749
+ note: "check the setup and list the commands that fix it"
8750
+ }, {
8751
+ command: `${runAsOf(cli)} doctor --json`,
8752
+ note: "get the rows and fixes as data"
8753
+ }],
8754
+ effect: "read",
8755
+ output: reportSchema,
8756
+ run: async (_input, ctx) => {
8757
+ const read = readProject(cli.project, ctx);
8758
+ const checked = (await Promise.all(doctorChecks(cli, launch, read).map((check) => rowOf(check, ctx, read)))).filter((row) => row !== void 0);
8759
+ const rows = checked.map(({ row }) => row);
8760
+ const report = {
8761
+ rows,
8762
+ next: nextOf(rows)
8763
+ };
8764
+ if (ctx.signal.aborted) {
8765
+ ctx.deliver(report);
8766
+ ctx.signal.throwIfAborted();
8767
+ }
8768
+ const problems = checked.filter(({ row, advisory }) => row.state === "problem" && !advisory).map(({ row }) => row);
8769
+ if (problems.length > 0) {
8770
+ ctx.deliver(report);
8771
+ throw failure(cli.name, ctx.commandLine(), problems);
8772
+ }
8773
+ return report;
8774
+ },
8775
+ render
8776
+ });
8777
+ }
8778
+ //#endregion
8748
8779
  //#region ../agent-cli/dist/built-ins/commands.js
8749
8780
  /**
8750
8781
  * The Commands the Runtime adds to a multi-Command CLI: `update` only for a Global install, and
8751
8782
  * `skill install` only for a Packaged skill.
8752
8783
  */
8753
8784
  function builtInCommands(cli, launch) {
8754
- const packaged = packagedFolderOf(cli);
8785
+ const install = installOf(cli);
8755
8786
  return {
8756
8787
  doctor: doctorCommand(cli, launch),
8757
- ...cli.install?.mode === "global" ? { update: updateCommand(cli, cli.install, launch) } : {},
8758
- ...packaged === void 0 ? {} : { skill: { install: skillInstallCommand(cli, packaged) } }
8788
+ ...install.builtIns(launch),
8789
+ ...skillOf(cli, install).builtIns
8759
8790
  };
8760
8791
  }
8761
8792
  //#endregion
@@ -8879,7 +8910,7 @@ function reachableExitClasses(cli) {
8879
8910
  "caller",
8880
8911
  "operator",
8881
8912
  "wait",
8882
- ...cli.commands !== void 0 && cli.install !== void 0 ? ["source"] : []
8913
+ ...cli.commands === void 0 ? [] : installOf(cli).exitClasses
8883
8914
  ];
8884
8915
  const own = Object.values(cli.errors ?? {}).map((declaration) => declaration.class);
8885
8916
  return /* @__PURE__ */ new Set([...runtime, ...own]);
@@ -8905,7 +8936,7 @@ function checkOwnCodes(cli) {
8905
8936
  function defineCli$1(cli) {
8906
8937
  checkOwnCodes(cli);
8907
8938
  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`);
8939
+ 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
8940
  return cli;
8910
8941
  }
8911
8942
  const { commands } = cli;
@@ -10023,7 +10054,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
10023
10054
  let served = false;
10024
10055
  let settled;
10025
10056
  try {
10026
- const outcome = await control.settle(noticeSkillCopy(cli, {
10057
+ const outcome = await control.settle(skillOf(cli, installOf(cli)).notice({
10027
10058
  builtIn,
10028
10059
  bin: options.bin
10029
10060
  }, ctx).then(() => execute(command, input, ctx, {
@@ -10222,14 +10253,14 @@ async function runProcess(cli) {
10222
10253
  }
10223
10254
  //#endregion
10224
10255
  //#region package.json
10225
- var version$1 = "0.15.0";
10256
+ var version$1 = "0.16.0";
10226
10257
  var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
10227
10258
  //#endregion
10228
10259
  //#region src/build.ts
10229
10260
  const build = {
10230
- "sha": "cd3df50",
10261
+ "sha": "648fa57",
10231
10262
  "dirty": false,
10232
- "builtAt": "2026-10-27T04:23:15.000Z"
10263
+ "builtAt": "2026-10-27T10:21:48.000Z"
10233
10264
  };
10234
10265
  //#endregion
10235
10266
  //#region src/fill.ts