@duyquangnvx/create-agent-cli 0.23.0 → 0.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duyquangnvx/create-agent-cli",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -21,7 +21,7 @@
21
21
  "access": "public"
22
22
  },
23
23
  "devDependencies": {
24
- "@duyquangnvx/agent-cli": "0.23.0",
24
+ "@duyquangnvx/agent-cli": "0.24.0",
25
25
  "tsdown": "0.23.0",
26
26
  "zod": "4.6.7"
27
27
  },
@@ -9,4 +9,4 @@ This installs the project's copy of {{binName}}. Always run it as `{{runAs}}`: a
9
9
  and with npm `! npm install -D {{packageName}}`.
10
10
 
11
11
  With yarn or bun, hand the user that package manager's own line.
12
- 2. Finish with [Check](#check): its `skill` row brings this skill to the installed version. {{binName}} is ready when `doctor` exits 0 with nothing in `next`.
12
+ 2. Finish with [Check](#check). {{binName}} is ready when `doctor` exits 0 with nothing in `next`.
@@ -1 +1 @@
1
- The `skill` row compares this skill's copy at the project root, `.claude/skills/{{binName}}`, with the one the project's {{binName}} ships. When the copy is missing or differs, as after an update, run its `{{runAs}} skill install` fix, then load this skill again. A copy under `~/.claude/skills/{{binName}}` shadows the project's: ask the user to remove it.
1
+ The `skill` row looks for the skill's copy at the project root, `.claude/skills/{{binName}}`. When the copy is missing or states `metadata.version`, as an older skill does, run its `{{runAs}} skill install` fix, then load the skill again. A copy under `~/.claude/skills/{{binName}}` shadows the project's: ask the user to remove it.
@@ -1,4 +1,4 @@
1
1
  1. Run `{{runAs}} doctor --json` and read its `registry` row. When it reports no newer release, {{binName}} is current: stop here. When a newer release exists, the row's fix is the line that moves {{packageName}} to it; with yarn or bun the row names the version without a line, so use that package manager's own line for it. If the row fails to reach the registry, the sandbox refused the host, as [Sandbox](#sandbox) says.
2
2
  2. Hand the user that line to type with `!`.
3
- 3. Read the `CHANGELOG.md` that `{{runAs}} --version` names, from the top down to the version the project left, and do each upgrade step it lists, oldest first.
4
- 4. Finish with [Check](#check): its `skill` row brings this skill to the new version.
3
+ 3. Read the `CHANGELOG.md` that `{{runAs}} --version` names, from the top down to the version the project left, and do each upgrade step it lists, oldest first. A release that changes the skill lists `{{runAs}} skill install` there.
4
+ 4. Finish with [Check](#check).
@@ -0,0 +1 @@
1
+ If the line above says {{binName}} is not installed, or names no `docs` folder, ask the user to type a line in the project's folder: `! pnpm install` when a `package.json` from here up lists {{packageName}}, else `! pnpm add -D {{packageName}}`. With npm these are `! npm install` and `! npm install -D {{packageName}}`, and with yarn or bun that package manager's own. Always run the CLI as `{{runAs}}`: a bare `npx {{binName}}` runs whatever npm package holds the name.
@@ -1,5 +1,5 @@
1
- Install the skill first, so that the coding agent loading it can install and check the CLI. In Claude Code, type each line in the prompt:
1
+ Paste this to your coding agent, with your task in it:
2
2
 
3
- {{skillLine}}
3
+ > Run `{{setupLine}}`, then use {{binName}} to <your task>.
4
4
 
5
- `! npm install --global {{packageName}}@latest`
5
+ That line installs the CLI and its skill. In a terminal it works the same.
@@ -1,10 +1,6 @@
1
- This installs the CLI. The user has installed this skill already.
1
+ This installs the CLI and this skill on this machine, and is safe to run again.
2
2
 
3
- 1. Run `node --version` and compare it with the range `npm view {{packageName}} engines.node` prints, which `doctor`'s `node` row also checks once the CLI is installed. Under nvm every Node version has its own global packages, so {{binName}} lands under the Node this shell runs. If that Node is too old, or is not the one the user's terminal runs, tell the user and let them choose before going on. That is also why a {{binName}} the user installed can be missing from this shell: ask which Node their terminal runs before installing again.
4
- 2. Hand the user the install line to type, as [Sandbox](#sandbox) says:
5
-
6
- `! npm install --global {{packageName}}@latest`
7
-
8
- Never run `npx {{binName}}`: that runs whatever npm package holds the name.
9
- 3. If `{{binName}} --version` then shows an older version than this skill describes, npm answered from a stale cache. Hand the user the same line with `--cache "$(mktemp -d)"` appended.
10
- 4. Finish with [Check](#check). {{binName}} is ready when `doctor` exits 0 with nothing in `next`: a row that only advises, such as `install`, leaves the exit code at 0.
3
+ 1. Run `{{setupLine}}`. Never run a bare `npx {{binName}}`: that runs whatever npm package holds the name.
4
+ 2. If it fails with a Next step, the sandbox may have refused it, as [Sandbox](#sandbox) says: hand the user that line to type with `!`.
5
+ 3. If it fails with `NOT_ON_PATH`, the CLI went under the Node this shell runs, which the shell cannot run bins from: ask which Node the user's terminal runs, and let them choose before going on. Under nvm each Node has its own global packages.
6
+ 4. Read the folder its `docs` line names, then go on with the task.
@@ -1,3 +1,3 @@
1
- Claude Code's sandbox never lets a command write npm's global folder, `~/.claude/skills` or `~/.agents/skills`, so the install line, `{{binName}} update` and `npx skills` fail inside it. Hand the user the line to type with `!` in this session, which runs it outside the sandbox under the Node this shell runs.
1
+ Claude Code's sandbox never lets a command write npm's global folder, `~/.claude/skills` or `~/.agents/skills`, so `{{setupLine}}` and `{{binName}} update` fail inside it. Hand the user the line to type with `!` in this session, which runs it outside the sandbox under the Node this shell runs.
2
2
 
3
3
  `doctor`'s `registry` row, `update` and `npm view` ask the registry {{binName}} is published to for its latest release. If the sandbox refuses that host, `doctor` still passes with only that row failing, and `update --dry-run` fails. If the user wants these to work inside the sandbox, give them this block for their Claude Code settings, naming the registry's host, and let them decide:
@@ -1 +1 @@
1
- The `skill` row names every installed copy of this skill against the installed CLI. A missing copy, or one that states no version, is installed afresh with the row's fix, since `npx skills update` skips a copy it did not install. After a fix that runs the `skills` installer, load this skill again.
1
+ The `skill` row looks for the skill's copies, `~/.claude/skills/{{binName}}` and `~/.agents/skills/{{binName}}`. A missing copy, or one stating `metadata.version`, as an older skill does, gets `{{binName}} setup`: run it, then load the skill again.
@@ -1,4 +1,3 @@
1
1
  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.
2
- 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.
3
- 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.
4
- 4. Confirm the new version with `{{binName}} --version`.
2
+ 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. It also brings the skill to that release, so load the skill again.
3
+ 3. Confirm the new version with `{{binName}} --version`.
@@ -0,0 +1 @@
1
+ If the line above says {{binName}} is not installed, or names no `docs` folder, run `{{setupLine}}`, which installs the CLI and this skill and names that folder; a bare `npx {{binName}}` runs whatever npm package holds the name. When a sandbox refuses it, ask the user to type that line with `!`. Under nvm each Node has its own global packages, so when the user says it is installed, ask which Node their terminal runs.
@@ -24,6 +24,8 @@ Follow the rules in `~/.claude/rules/harness/`. Biome enforces the mechanical on
24
24
 
25
25
  ## Agent skills
26
26
 
27
- `skills/{{binName}}/SKILL.md` is the agent skill: when to use `{{binName}}`, its workflows and how to recover from its errors. Update it with any change to those, and leave flag details to `--help`. Its `references/setup.md` installs, checks and updates `{{binName}}`; `test/cli.test.ts` fails when its install line, or the README's, differs from what `doctor` gives.
27
+ `skills/{{binName}}/SKILL.md` is the CLI skill: when to use `{{binName}}`, its install line and a pointer to `docs/cli/`, so it stays true across releases. `{{runAs}} update` brings a Global install's copy along; a Dependency install's release that changes it lists `{{runAs}} skill install` under `### Upgrade from` in `CHANGELOG.md`. `test/cli.test.ts` fails when its install line, the README's or `docs/cli/setup.md`'s differs from what `doctor` gives.
28
+
29
+ `docs/cli/` is the CLI docs, which ship in the package and `--version` names: `README.md` holds the workflows and how to recover from errors, `setup.md` checks and updates `{{binName}}`. Update them with any change to those, and leave flag details to `--help`.
28
30
 
29
31
  {{agentsSection}}
@@ -0,0 +1,34 @@
1
+ <!-- A skeleton to rewrite around {{binName}}'s real Commands. A line earns its place only when no `--help`, output, hint or `doctor` row tells the agent the same thing: write down what costs it turns to work out, such as a convention, a reason, a trap, or a step that needs the user's consent. Leave flags out: `--help` is generated from the code and stays in sync with it, this file does not. -->
2
+
3
+ # {{binName}}
4
+
5
+ `{{runAs}} --help` lists the Commands, and `{{runAs}} <command> --help` shows a Command's examples, args, options and the flags the Runtime adds. This folder ships with the CLI, so it describes the build that runs. It covers the work around the Commands.
6
+
7
+ To check, update or fix the install, read [setup](setup.md).
8
+
9
+ ## Workflows
10
+
11
+ <!-- Each main task as steps naming the Commands it runs, in order. Move a long workflow to its own page in this folder and link it. -->
12
+
13
+ ### Greet someone
14
+
15
+ 1. Run `{{runAs}} greet <name> --json`.
16
+ 2. Read `greeting` from the JSON on stdout.
17
+
18
+ ### Write a card
19
+
20
+ 1. Run `{{runAs}} card <name> --language <code> --dry-run` to see the file it writes and what it costs.
21
+ 2. If it states a cost, ask the user before going on.
22
+ 3. Run it again without `--dry-run`, adding `--yes` once the user approved the cost.
23
+
24
+ To put a message under the greeting, pass `-` after the name and pipe the message in with a heredoc, so its quotes and newlines reach the card intact.
25
+
26
+ ## Recovering from errors
27
+
28
+ Do what the error's hint says, except for a code below:
29
+
30
+ | Code | What to do |
31
+ |---|---|
32
+ | `LANGUAGE_UNSUPPORTED` | The user asked for that language. Ask whether an English card will do before running the Next step. |
33
+
34
+ <!-- Add a row for each code {{binName}} throws whose right move goes beyond its hint. A guardrail on a flag goes in the workflow step that passes it instead. -->
@@ -1,6 +1,6 @@
1
1
  # Setup
2
2
 
3
- Checking, installing and updating {{binName}}, and letting it work inside Claude Code's sandbox. Using it is in [SKILL.md](../SKILL.md).
3
+ Checking, installing and updating {{binName}}, and letting it work inside Claude Code's sandbox. Using it is in [README.md](README.md).
4
4
 
5
5
  Work through a section in order and stop at a step that fails. Ask the user to do what only they can do, such as typing an install line, and wait until they say it is done.
6
6
 
@@ -12,6 +12,8 @@
12
12
  },
13
13
  "files": [
14
14
  "dist",
15
+ "docs/cli",
16
+ "skills",
15
17
  "CHANGELOG.md"
16
18
  ],
17
19
  "publishConfig": {
@@ -29,7 +31,7 @@
29
31
  "@biomejs/biome": "2.5.15",
30
32
  "@duyquangnvx/agent-cli": "{{runtimeVersion}}",
31
33
  "@duyquangnvx/harness": "0.3.1",
32
- "@duyquangnvx/release": "0.5.0",
34
+ "@duyquangnvx/release": "0.9.0",
33
35
  "@types/node": "^22.19.0",
34
36
  "tsdown": "0.23.0",
35
37
  "typescript": "7.0.2",
@@ -1,6 +1,5 @@
1
1
  import { release } from "@duyquangnvx/release";
2
2
 
3
3
  process.exitCode = await release({
4
- skill: "skills/{{binName}}/SKILL.md",
5
4
  smoke: { cli: true },
6
5
  });
@@ -2,46 +2,15 @@
2
2
  name: {{binName}}
3
3
  description: >-
4
4
  {{description}} Use when a task needs what `{{binName}}` does, or when a `{{binName}}` command fails.
5
- allowed-tools: Bash({{runAs}} --version)
6
- metadata:
7
- version: "0.0.0"
5
+ allowed-tools: Bash({{runAs}} --version) Read(//**/node_modules/{{packageName}}/docs/cli/**)
8
6
  ---
9
7
 
10
- <!-- A skeleton to rewrite around {{binName}}'s real Commands. A line earns its place only when no `--help`, output, hint or `doctor` row tells the agent the same thing: write down what costs it turns to work out, such as a convention, a reason, a trap, or a step that needs the user's consent. Leave flags out: `--help` is generated from the code and stays in sync with it, this file does not. What {{binName}} is for, and the nearby tasks it is not for, go in the description, which the agent always sees. -->
8
+ <!-- This file holds only what stays true across releases, so a release need not change it: say here when to use {{binName}}, in the description, and put how to use it in docs/cli/. -->
11
9
 
12
10
  # {{binName}}
13
11
 
14
- `{{runAs}} --help` lists the Commands, and `{{runAs}} <command> --help` shows a Command's examples, args, options and the flags the Runtime adds. This file covers the work around them.
15
-
16
- ## Installed CLI
17
-
18
12
  !`{{runAs}} --version 2>&1 || echo "{{binName}}: not installed"`
19
13
 
20
- The line above should show 0.0.0, the version this skill describes. If it says {{binName}} is not installed or shows another version, read [setup](references/setup.md) before anything else.
21
-
22
- ## Workflows
23
-
24
- <!-- Each main task as steps naming the Commands it runs, in order. Move a long workflow to a file under references/ and link it. -->
25
-
26
- ### Greet someone
27
-
28
- 1. Run `{{runAs}} greet <name> --json`.
29
- 2. Read `greeting` from the JSON on stdout.
30
-
31
- ### Write a card
32
-
33
- 1. Run `{{runAs}} card <name> --language <code> --dry-run` to see the file it writes and what it costs.
34
- 2. If it states a cost, ask the user before going on.
35
- 3. Run it again without `--dry-run`, adding `--yes` once the user approved the cost.
36
-
37
- To put a message under the greeting, pass `-` after the name and pipe the message in with a heredoc, so its quotes and newlines reach the card intact.
38
-
39
- ## Recovering from errors
40
-
41
- Do what the error's hint says, except for a code below:
42
-
43
- | Code | What to do |
44
- |---|---|
45
- | `LANGUAGE_UNSUPPORTED` | The user asked for that language. Ask whether an English card will do before running the Next step. |
14
+ Before any other `{{binName}}` command, read the folder the `docs` line above names, starting with its `README.md` when it has one: it holds the steps to follow and what to do on each error.
46
15
 
47
- <!-- Add a row for each code {{binName}} throws whose right move goes beyond its hint. A guardrail on a flag goes in the workflow step that passes it instead. -->
16
+ {{whenMissing}}
@@ -1,5 +1,23 @@
1
1
  #!/usr/bin/env node
2
- import { runProcess } from "@duyquangnvx/agent-cli";
3
- import { cli } from "./cli.js";
2
+ import { readFileSync } from "node:fs";
4
3
 
4
+ // Every other import waits for this check, so a Node too old to load them fails with what it needs.
5
+ const manifest: { name?: string; engines?: { node?: string } } = JSON.parse(
6
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
7
+ );
8
+ const range = manifest.engines?.node ?? ">=0";
9
+ const needed = range.replace(">=", "").split(".").map(Number);
10
+ const running = process.versions.node.split(".").map(Number);
11
+ const differs = needed.findIndex((part, index) => part !== running[index]);
12
+ if (differs !== -1 && (running[differs] ?? 0) < (needed[differs] ?? 0)) {
13
+ process.stderr.write(
14
+ `${manifest.name} needs Node ${range}, and this is ${process.versions.node}: install it, then run this again\n`,
15
+ );
16
+ process.exit(2);
17
+ }
18
+
19
+ const [{ runProcess }, { cli }] = await Promise.all([
20
+ import("@duyquangnvx/agent-cli"),
21
+ import("./cli.js"),
22
+ ]);
5
23
  await runProcess(cli);
@@ -101,5 +101,7 @@ const card = defineCommand({
101
101
  export const cli = defineCli({
102
102
  manifest,
103
103
  install: "global",
104
+ docs: "docs/cli",
105
+ skill: { folder: "skills/{{binName}}" },
104
106
  commands: { greet, card },
105
107
  });
@@ -199,15 +199,17 @@ describe("install lines", () => {
199
199
  return readFile(join(root, path), "utf8");
200
200
  }
201
201
 
202
- test("the setup reference and the README install the CLI with doctor's install line", async () => {
202
+ test("the skill, the setup page and the README install the CLI with doctor's install line", async () => {
203
203
  const install = (await doctorRows()).install?.fix;
204
204
 
205
205
  expect(install).toBeDefined();
206
- expect(await text("skills/{{binName}}/references/setup.md")).toContain(`\`! ${install}\``);
207
- expect(await text("README.md")).toContain(`\`! ${install}\``);
206
+ expect(await text("skills/{{binName}}/SKILL.md")).toContain(install);
207
+ expect(await text("docs/cli/setup.md")).toContain(install);
208
+ expect(await text("README.md")).toContain(install);
208
209
  });
209
210
 
210
- test.skipIf(cli.skill === undefined)(
211
+ // A Global install's line that also installs the skill is doctor's install line, checked above.
212
+ test.skipIf(DOCTOR_BIN === "")(
211
213
  "the README installs the skill with doctor's skill line",
212
214
  async () => {
213
215
  const fix = (await doctorRows()).skill?.fix;
@@ -217,11 +219,16 @@ describe("install lines", () => {
217
219
  },
218
220
  );
219
221
 
220
- test("the setup reference names the registry's host for the sandbox to allow", async () => {
222
+ test("the skill's read grant and the package's files name the CLI docs folder", async () => {
223
+ const skill = await text("skills/{{binName}}/SKILL.md");
224
+
225
+ expect(skill).toContain(`Read(//**/node_modules/${manifest.name}/${cli.docs}/**)`);
226
+ expect(manifest.files).toContain(cli.docs);
227
+ });
228
+
229
+ test("the setup page names the registry's host for the sandbox to allow", async () => {
221
230
  const { host } = new URL(manifest.publishConfig.registry);
222
231
 
223
- expect(await text("skills/{{binName}}/references/setup.md")).toContain(
224
- `"allowedDomains": ["${host}"]`,
225
- );
232
+ expect(await text("docs/cli/setup.md")).toContain(`"allowedDomains": ["${host}"]`);
226
233
  });
227
234
  });