@duyquangnvx/create-agent-cli 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.9.0 (2026-10-20)
8
+
9
+ ### Added
10
+
11
+ - The Scaffolder's `--skill-hub <git URL>` names the Skill Hub each release commits the new CLI's skill to: it becomes `skill.source` in `defineCli`, `skillHub` in `scripts/release.ts` and the README's skill line. See ADR 0009. It refuses a URL that does not end in `.git` or holds a query or fragment, and with it a name the release cannot put in a hub (`SKILL_NAME_INVALID`).
12
+
13
+ ### Changed
14
+
15
+ - **Breaking:** `doctor`'s `skill` row and `update`'s `next` install the skill only from a declared `skill.source`, no longer from package.json `repository` at `#release`, a branch `@duyquangnvx/release` 0.4 stops moving. A CLI without `skill.source` still gets `skills update` for an older copy.
16
+ - **Breaking:** the Scaffolder's `--repository` only sets package.json `repository`; the skill line comes from `--skill-hub`. The scaffolded template releases with `@duyquangnvx/release` 0.4.0.
17
+ - **Breaking:** the agent-cli skill installs from the Skill Hub: `npx skills add https://github.com/duyquangnvx/skills.git --skill agent-cli -g`.
18
+
19
+ ## v0.8.0 (2026-10-20)
20
+
21
+ ### Changed
22
+
23
+ - **Breaking:** `update` shows only the Upgrade steps of a skipped release whose CHANGELOG section lists them under `### Upgrade from v<version>`, under `upgrade steps for <version>:`. A release whose only step is `Nothing to do.` is left out, and one without the heading still shows its whole section under `changes in <version>:`.
24
+ - **Breaking:** each entry of `upgrades` in `update --json` is `{ version, steps }` or `{ version, notes }`.
25
+ - The scaffolded template's CHANGELOG asks each release from the second on to hold `### Upgrade from v<the release below>`, and its setup reference tells the agent to do the steps `update` prints. The agent-cli skill's shipping reference explains the convention.
26
+
7
27
  ## v0.7.0 (2026-10-20)
8
28
 
9
29
  ### Added
package/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
  Creates a new Agent CLI repository already wired to [`@duyquangnvx/agent-cli`](https://www.npmjs.com/package/@duyquangnvx/agent-cli):
4
4
 
5
5
  ```sh
6
- pnpm create @duyquangnvx/agent-cli <name> --description "One line saying what it does" --repository https://github.com/<owner>/<name>
6
+ pnpm create @duyquangnvx/agent-cli <name> --description "One line saying what it does" --skill-hub https://github.com/<owner>/skills.git
7
7
  ```
8
8
 
9
- The new repository has an Error table, a port, two example Commands (one writing a file at a cost) and their test, a bundled build that stamps the commit and build time into `--version`, CI, a release script, a CHANGELOG, an `AGENTS.md`, and a skeleton skill for the new CLI whose setup reference installs, checks and updates it, so a user installs the skill first and lets their agent do the rest. `--repository` names where that skill is installed from. Pass `--dry-run` to see what it would write.
9
+ The new repository has an Error table, a port, two example Commands (one writing a file at a cost) and their test, a bundled build that stamps the commit and build time into `--version`, CI, a release script, a CHANGELOG, an `AGENTS.md`, and a skeleton skill for the new CLI whose setup reference installs, checks and updates it, so a user installs the skill first and lets their agent do the rest. `--skill-hub` names the Skill Hub each release commits that skill to and agents install it from. Pass `--dry-run` to see what it would write.
10
10
 
11
- See the [repository](https://github.com/duyquangnvx/cli-for-agent) for an overview and the [agent-cli skill](https://github.com/duyquangnvx/cli-for-agent/blob/release/skills/agent-cli/SKILL.md) for how to build on it.
11
+ See the [repository](https://github.com/duyquangnvx/cli-for-agent) for an overview and the [agent-cli skill](https://github.com/duyquangnvx/cli-for-agent/blob/main/skills/agent-cli/SKILL.md) for how to build on it.
package/dist/bin.js CHANGED
@@ -7049,36 +7049,8 @@ async function globalInstallOf(bin, ctx) {
7049
7049
  };
7050
7050
  }
7051
7051
  //#endregion
7052
- //#region ../agent-cli/dist/manifest.js
7053
- const link$1 = union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0);
7054
- const manifestSchema = object({
7055
- repository: link$1,
7056
- bugs: link$1
7057
- });
7058
- const NO_MANIFEST = {};
7059
- function manifestAt(packageRoot) {
7060
- let manifest;
7061
- try {
7062
- manifest = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
7063
- } catch {
7064
- return NO_MANIFEST;
7065
- }
7066
- const parsed = manifestSchema.safeParse(manifest);
7067
- return parsed.success ? parsed.data : NO_MANIFEST;
7068
- }
7069
- //#endregion
7070
7052
  //#region ../agent-cli/dist/built-ins/skills.js
7071
7053
  const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
7072
- /** The branch a release moves to the released commit, which holds the skill of that version. */
7073
- const RELEASE_BRANCH$1 = "release";
7074
- const SHORTHAND = /^(?:(?:github:)?[\w.-]+\/[\w.-]+|gitlab:[\w.-]+(?:\/[\w.-]+)+)$/;
7075
- const GIT_URL_PROTOCOLS = /* @__PURE__ */ new Set([
7076
- "https:",
7077
- "http:",
7078
- "ssh:",
7079
- "git:"
7080
- ]);
7081
- const SCP_STYLE = /^[\w.-]+@[\w.-]+:[\w./-]+$/;
7082
7054
  const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
7083
7055
  const METADATA_VERSION = /^metadata:[ \t]*\r?\n(?:[ \t]+.*\r?\n)*?[ \t]+version:[ \t]*["']?([^"'\s]+)["']?[ \t]*$/m;
7084
7056
  /** The version a SKILL.md's frontmatter `metadata.version` states. */
@@ -7127,18 +7099,6 @@ async function skillCopies(cliName, version, { cwd, env }) {
7127
7099
  }
7128
7100
  return copies;
7129
7101
  }
7130
- function sourceFromRepository(repository) {
7131
- if (SHORTHAND.test(repository)) return `${repository}#${RELEASE_BRANCH$1}`;
7132
- const url = repository.replace(/^git\+/, "");
7133
- if (!(URL.canParse(url) && GIT_URL_PROTOCOLS.has(new URL(url).protocol)) && !SCP_STYLE.test(url)) return void 0;
7134
- return `${url.endsWith(".git") ? url : `${url}.git`}#${RELEASE_BRANCH$1}`;
7135
- }
7136
- /** What the installer installs the CLI's skill from: the declared source, else the package's repository. */
7137
- function skillSource({ skill }, packageRoot) {
7138
- if (skill !== void 0) return skill.source;
7139
- const { repository } = manifestAt(packageRoot);
7140
- return repository === void 0 ? void 0 : sourceFromRepository(repository);
7141
- }
7142
7102
  function fixFor(cliName, copies, source) {
7143
7103
  const has = (wanted) => copies.some(({ state }) => state === wanted);
7144
7104
  const update = `npx -y skills update ${cliName}`;
@@ -7151,7 +7111,7 @@ async function skillReport(cli, version, ctx) {
7151
7111
  const copies = await skillCopies(cli.name, version, ctx);
7152
7112
  return {
7153
7113
  copies,
7154
- fix: fixFor(cli.name, copies, skillSource(cli, ctx.packageRoot))
7114
+ fix: fixFor(cli.name, copies, cli.skill?.source)
7155
7115
  };
7156
7116
  }
7157
7117
  //#endregion
@@ -7554,8 +7514,20 @@ var RunControl = class {
7554
7514
  }
7555
7515
  };
7556
7516
  //#endregion
7557
- //#region ../agent-cli/dist/built-ins/upgrade-notes.js
7517
+ //#region ../agent-cli/dist/built-ins/upgrades.js
7518
+ /** A skipped release's Upgrade steps, or its whole section when it lists none. */
7519
+ const upgradeSchema = union([object({
7520
+ version: string(),
7521
+ steps: array(string())
7522
+ }), object({
7523
+ version: string(),
7524
+ notes: string()
7525
+ })]);
7558
7526
  const RELEASE_HEADING = /^## v?(\S+)/;
7527
+ const UPGRADE_HEADING = /^### Upgrade from v\S+$/;
7528
+ const HEADING = /^#{1,6} /;
7529
+ const ITEM = /^[-*] /;
7530
+ const NOTHING_TO_DO = /^nothing to do\.?$/i;
7559
7531
  function releasesIn(changelog) {
7560
7532
  const releases = [];
7561
7533
  let current;
@@ -7570,16 +7542,33 @@ function releasesIn(changelog) {
7570
7542
  if (current !== void 0) releases.push(current);
7571
7543
  } else current?.lines.push(line);
7572
7544
  }
7573
- return releases.map(({ version, lines }) => ({
7545
+ return releases;
7546
+ }
7547
+ function listedSteps(lines) {
7548
+ const start = lines.findIndex((line) => UPGRADE_HEADING.test(line.trimEnd()));
7549
+ if (start === -1) return [];
7550
+ const end = lines.findIndex((line, i) => i > start && HEADING.test(line));
7551
+ const body = lines.slice(start + 1, end === -1 ? void 0 : end).filter((line) => line.trim() !== "");
7552
+ const starts = body.flatMap((line, i) => ITEM.test(line) ? [i] : []);
7553
+ return starts.map((first, i) => body.slice(first, starts[i + 1]).map((line) => line.trim()).join(" ").replace(ITEM, ""));
7554
+ }
7555
+ function upgradeOf({ version, lines }) {
7556
+ const listed = listedSteps(lines);
7557
+ if (listed.length === 0) return [{
7574
7558
  version,
7575
7559
  notes: lines.join("\n").trim()
7576
- }));
7560
+ }];
7561
+ const todo = listed.filter((step) => !NOTHING_TO_DO.test(step));
7562
+ return todo.length === 0 ? [] : [{
7563
+ version,
7564
+ steps: todo
7565
+ }];
7577
7566
  }
7578
7567
  /**
7579
- * What changed in each release after `from` up to `to`, oldest first, from the CHANGELOG.md in
7580
- * `packageDir`; none when the package ships no CHANGELOG.
7568
+ * What each release after `from` up to `to` asks of the operator, oldest first, from the
7569
+ * CHANGELOG.md in `packageDir`; none when the package ships no CHANGELOG.
7581
7570
  */
7582
- async function upgradeNotes(packageDir, from, to) {
7571
+ async function upgradesOf(packageDir, from, to) {
7583
7572
  let changelog;
7584
7573
  try {
7585
7574
  changelog = await readFile(join(packageDir, "CHANGELOG.md"), "utf8");
@@ -7587,7 +7576,7 @@ async function upgradeNotes(packageDir, from, to) {
7587
7576
  if (isMissingFile(error)) return [];
7588
7577
  throw error;
7589
7578
  }
7590
- return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version));
7579
+ return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version)).flatMap(upgradeOf);
7591
7580
  }
7592
7581
  //#endregion
7593
7582
  //#region ../agent-cli/dist/built-ins/update.js
@@ -7598,18 +7587,18 @@ const updateSchema = object({
7598
7587
  version: string(),
7599
7588
  prefix: string()
7600
7589
  }).nullable(),
7601
- upgrades: array(object({
7602
- version: string(),
7603
- notes: string()
7604
- })),
7590
+ upgrades: array(upgradeSchema),
7605
7591
  next: array(string())
7606
7592
  });
7607
7593
  function nothingToInstall({ from, latest }) {
7608
7594
  return standingOf(from, latest) === "current" ? `already on ${from}, the latest release: nothing to install` : `${from} is newer than the latest release ${latest}: nothing to install`;
7609
7595
  }
7596
+ function renderUpgrade(upgrade) {
7597
+ return "steps" in upgrade ? [`\nupgrade steps for ${upgrade.version}:`, ...upgrade.steps.map((step) => `- ${step}`)].join("\n") : `\nchanges in ${upgrade.version}:\n${upgrade.notes}`;
7598
+ }
7610
7599
  function renderUpdate(cli, install, update) {
7611
7600
  const { installed } = update;
7612
- const head = installed === null ? [nothingToInstall(update)] : [`installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`, ...update.upgrades.map(({ version, notes }) => `\nchanges in ${version}:\n${notes}`)];
7601
+ const head = installed === null ? [nothingToInstall(update)] : [`installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`, ...update.upgrades.map(renderUpgrade)];
7613
7602
  const next = update.next.map((command) => `next: ${command}, so the ${cli.name} skill describes ${update.latest}`);
7614
7603
  return (next.length === 0 ? head : [
7615
7604
  ...head,
@@ -7672,7 +7661,7 @@ function updateCommand(cli, install, { bin, node }) {
7672
7661
  version: latest,
7673
7662
  prefix
7674
7663
  },
7675
- upgrades: await upgradeNotes(plan.packageDir, from, latest),
7664
+ upgrades: await upgradesOf(plan.packageDir, from, latest),
7676
7665
  next: await skillNext(cli, latest, ctx)
7677
7666
  };
7678
7667
  if (ctx.signal.aborted) {
@@ -8756,6 +8745,18 @@ async function execute(command, input, ctx, { dryRun, confirm, yes, page, invoca
8756
8745
  }
8757
8746
  return outcomeOf(await command.apply(plan, ctx), (data, style) => command.render(data, style));
8758
8747
  }
8748
+ const manifestSchema = object({ bugs: union([string(), object({ url: string() })]).transform((value) => typeof value === "string" ? value : value.url).optional().catch(void 0) });
8749
+ const NO_MANIFEST = {};
8750
+ function manifestAt(packageRoot) {
8751
+ let manifest;
8752
+ try {
8753
+ manifest = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
8754
+ } catch {
8755
+ return NO_MANIFEST;
8756
+ }
8757
+ const parsed = manifestSchema.safeParse(manifest);
8758
+ return parsed.success ? parsed.data : NO_MANIFEST;
8759
+ }
8759
8760
  //#endregion
8760
8761
  //#region ../agent-cli/dist/parse-input.js
8761
8762
  const NO_INPUT = object({});
@@ -9408,14 +9409,14 @@ async function runProcess(cli) {
9408
9409
  }
9409
9410
  //#endregion
9410
9411
  //#region package.json
9411
- var version$1 = "0.7.0";
9412
+ var version$1 = "0.9.0";
9412
9413
  var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
9413
9414
  //#endregion
9414
9415
  //#region src/build.ts
9415
9416
  const build = {
9416
- "sha": "10574f5",
9417
+ "sha": "ff9a1b4",
9417
9418
  "dirty": false,
9418
- "builtAt": "2026-10-20T03:42:41.670Z"
9419
+ "builtAt": "2026-10-20T09:52:22.284Z"
9419
9420
  };
9420
9421
  //#endregion
9421
9422
  //#region src/steps.ts
@@ -9549,8 +9550,17 @@ const version = version$1;
9549
9550
  //#region src/create.ts
9550
9551
  const { defineCommand } = agentCli();
9551
9552
  const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?([a-z0-9][a-z0-9._-]*)$/;
9552
- const RELEASE_BRANCH = "release";
9553
- const NO_REPOSITORY = "<!-- Set `repository` in package.json, then put here the `! npx -y skills add` line that `node dist/bin.js doctor` gives as the `skill` row's fix. -->";
9553
+ const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
9554
+ const NO_SKILL_HUB = "<!-- Set `skill.source` in `defineCli` and `skillHub` in `scripts/release.ts` to the git URL of the Skill Hub each release commits the skill to, then put here the `! npx -y skills add` line that `node dist/bin.js doctor` gives as the `skill` row's fix. -->";
9555
+ const SKILL_HUB_ANCHORS = [{
9556
+ file: "src/cli.ts",
9557
+ before: " commands: {",
9558
+ line: (hub) => ` skill: { source: ${JSON.stringify(hub)} },\n`
9559
+ }, {
9560
+ file: "scripts/release.ts",
9561
+ before: " smoke: {",
9562
+ line: (hub) => ` skillHub: ${JSON.stringify(hub)},\n`
9563
+ }];
9554
9564
  function errorCode(error) {
9555
9565
  return error instanceof Error && "code" in error ? error.code : void 0;
9556
9566
  }
@@ -9563,9 +9573,16 @@ async function isFree(path) {
9563
9573
  throw error;
9564
9574
  }
9565
9575
  }
9566
- function skillInstallLine(repository, binName) {
9567
- if (repository === void 0) return NO_REPOSITORY;
9568
- return `\`! npx -y skills add ${`${repository.endsWith(".git") ? repository : `${repository}.git`}#${RELEASE_BRANCH}`} --skill ${binName} -g\``;
9576
+ function skillInstallLine(skillHub, binName) {
9577
+ return skillHub === void 0 ? NO_SKILL_HUB : `\`! npx -y skills add ${skillHub} --skill ${binName} -g\``;
9578
+ }
9579
+ async function setSkillHub(root, skillHub) {
9580
+ for (const { file, before, line } of SKILL_HUB_ANCHORS) {
9581
+ const path = join(root, file);
9582
+ const text = await readFile(path, "utf8");
9583
+ if (!text.includes(before)) throw new Error(`the template's ${file} has no \`${before}\` line to put the Skill Hub before`);
9584
+ await writeFile(path, text.replace(before, `${line(skillHub)}${before}`));
9585
+ }
9569
9586
  }
9570
9587
  const MANIFEST = record(string(), unknown());
9571
9588
  async function setRepository(root, repository) {
@@ -9574,6 +9591,13 @@ async function setRepository(root, repository) {
9574
9591
  const entries = Object.entries(manifest).flatMap((entry) => entry[0] === "description" ? [entry, ["repository", repository]] : [entry]);
9575
9592
  await writeFile(path, `${JSON.stringify(Object.fromEntries(entries), null, 2)}\n`);
9576
9593
  }
9594
+ function ensureSkillName(binName) {
9595
+ if (!SKILL_NAME.test(binName)) throw new UsageError({
9596
+ code: "SKILL_NAME_INVALID",
9597
+ message: `${binName} cannot name a skill in a Skill Hub, whose names are lowercase letters, digits and single hyphens`,
9598
+ hint: "choose a name such as my-cli, or leave out --skill-hub"
9599
+ });
9600
+ }
9577
9601
  async function ensureFree(path) {
9578
9602
  if (!await isFree(path)) throw new UsageError({
9579
9603
  code: "TARGET_NOT_EMPTY",
@@ -9594,10 +9618,12 @@ const create = defineCommand({
9594
9618
  description: string().default("An Agent CLI.").describe("one line saying what the CLI does"),
9595
9619
  install: boolean().default(true).describe("run pnpm install"),
9596
9620
  git: boolean().default(true).describe("create a git repository with an initial commit"),
9597
- repository: url({ protocol: /^https$/ }).optional().describe("the https URL of the CLI's git repository, where its skill is installed from")
9621
+ repository: url({ protocol: /^https$/ }).optional().describe("the https URL of the CLI's git repository"),
9622
+ skillHub: url({ protocol: /^https$/ }).refine((url) => new URL(url).search === "" && new URL(url).hash === "", "must name the hub's repository with no query or fragment").refine((url) => new URL(url).pathname.endsWith(".git"), "must end in .git").optional().describe("the https git URL of the Skill Hub each release commits the CLI's skill to")
9598
9623
  }),
9599
- plan: async ({ name, description, install, git, repository }, ctx) => {
9624
+ plan: async ({ name, description, install, git, repository, skillHub }, ctx) => {
9600
9625
  const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
9626
+ if (skillHub !== void 0) ensureSkillName(binName);
9601
9627
  const path = resolve(ctx.cwd, binName);
9602
9628
  await ensureFree(path);
9603
9629
  const values = {
@@ -9606,7 +9632,7 @@ const create = defineCommand({
9606
9632
  description,
9607
9633
  debugVariable: debugVariable(binName),
9608
9634
  runtimeVersion: version,
9609
- skillInstall: skillInstallLine(repository, binName)
9635
+ skillInstall: skillInstallLine(skillHub, binName)
9610
9636
  };
9611
9637
  return {
9612
9638
  path,
@@ -9617,13 +9643,15 @@ const create = defineCommand({
9617
9643
  git
9618
9644
  }),
9619
9645
  install,
9620
- repository
9646
+ repository,
9647
+ skillHub
9621
9648
  };
9622
9649
  },
9623
9650
  apply: async (plan, ctx) => {
9624
9651
  await mkdir(plan.path, { recursive: true });
9625
9652
  await writeTemplate(plan.path, plan.files, plan.values);
9626
9653
  if (plan.repository !== void 0) await setRepository(plan.path, plan.repository);
9654
+ if (plan.skillHub !== void 0) await setSkillHub(plan.path, plan.skillHub);
9627
9655
  await runSteps(plan.steps, plan.path, ctx);
9628
9656
  return {
9629
9657
  path: plan.path,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duyquangnvx/create-agent-cli",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -19,7 +19,7 @@
19
19
  "access": "public"
20
20
  },
21
21
  "devDependencies": {
22
- "@duyquangnvx/agent-cli": "0.7.0",
22
+ "@duyquangnvx/agent-cli": "0.9.0",
23
23
  "tsdown": "0.23.0",
24
24
  "zod": "4.6.7"
25
25
  },
@@ -1,5 +1,5 @@
1
1
  # Changelog
2
2
 
3
- What changed in each release of `{{packageName}}`, newest first, in the format of [Keep a Changelog 2.0](https://keepachangelog.com/en/2.0.0/). Each change lands under `## Unreleased` as it merges, grouped under `### Added`, `### Changed`, `### Fixed` or `### Removed`. A breaking change starts with `**Breaking:**` and names what breaks: a Command, a flag, a `--json` field, an exit code or the skill. A release turns `## Unreleased` into `## vX.Y.Z (YYYY-MM-DD)`.
3
+ What changed in each release of `{{packageName}}`, newest first, in the format of [Keep a Changelog 2.0](https://keepachangelog.com/en/2.0.0/). Each change lands under `## Unreleased` as it merges, grouped under `### Added`, `### Changed`, `### Fixed` or `### Removed`. A breaking change starts with `**Breaking:**` and names what breaks: a Command, a flag, a `--json` field, an exit code or the skill. A release turns `## Unreleased` into `## vX.Y.Z (YYYY-MM-DD)`. From the second release on, a release holds `### Upgrade from v<the release below>` listing what someone upgrading must do, or `- Nothing to do.`.
4
4
 
5
5
  ## Unreleased
@@ -29,7 +29,7 @@
29
29
  "@biomejs/biome": "2.5.15",
30
30
  "@duyquangnvx/agent-cli": "{{runtimeVersion}}",
31
31
  "@duyquangnvx/harness": "0.3.1",
32
- "@duyquangnvx/release": "0.2.0",
32
+ "@duyquangnvx/release": "0.4.0",
33
33
  "@types/node": "^22.19.0",
34
34
  "tsdown": "0.23.0",
35
35
  "typescript": "7.0.2",
@@ -1,4 +1,5 @@
1
- # A fresh project pins the Runtime release it was created with, which can be younger than
2
- # pnpm's minimum release age.
1
+ # A fresh project pins the Runtime and release versions it was created with, which can be younger
2
+ # than pnpm's minimum release age.
3
3
  minimumReleaseAgeExclude:
4
4
  - "@duyquangnvx/agent-cli"
5
+ - "@duyquangnvx/release"
@@ -32,7 +32,7 @@ This installs the CLI. The user has installed this skill already.
32
32
  ## Update
33
33
 
34
34
  1. Run `{{binName}} update --dry-run` to see the version it would install. If it fails to reach the registry, the sandbox refused the host, as [Sandbox](#sandbox) says: go on to the next step.
35
- 2. Ask the user to type `! {{binName}} update`; never run it yourself. It installs the latest release and prints what changed since the version it replaced: under `--json`, `upgrades` lists each release's notes, oldest first. Do each upgrade step those notes give, oldest first.
35
+ 2. Ask the user to type `! {{binName}} update`; never run it yourself. It installs the latest release and prints, for each release since the version it replaced, oldest first, the upgrade steps it asks of the user, or its whole notes when it lists none: under `--json`, each entry of `upgrades` holds `steps` or `notes`. Do each step, oldest first, and any the notes give.
36
36
  3. Hand the user each line in `next` to type with `!`: they bring this skill to the version `update` installed, or to the latest release when it installed nothing, as `doctor`'s `skill` row would. Load this skill again after, as [Check](#check) says.
37
37
  4. Confirm the new version with `{{binName}} --version`.
38
38
 
@@ -186,7 +186,7 @@ describe("install lines", () => {
186
186
  expect(await text("README.md")).toContain(`\`! ${install}\``);
187
187
  });
188
188
 
189
- test.skipIf(!("repository" in manifest))(
189
+ test.skipIf(cli.skill === undefined)(
190
190
  "the README installs the skill with doctor's skill line",
191
191
  async () => {
192
192
  const { skill } = await doctorFixes();