@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/CHANGELOG.md +31 -0
- package/README.md +2 -2
- package/dist/bin.js +1031 -1029
- package/package.json +2 -2
- package/partials/dependency/installSteps.md +1 -1
- package/partials/dependency/skillCheck.md +1 -1
- package/partials/dependency/updateSteps.md +2 -2
- package/partials/dependency/whenMissing.md +1 -0
- package/partials/global/installLines.md +3 -3
- package/partials/global/installSteps.md +5 -9
- package/partials/global/sandbox.md +1 -1
- package/partials/global/skillCheck.md +1 -1
- package/partials/global/updateSteps.md +2 -3
- package/partials/global/whenMissing.md +1 -0
- package/template/AGENTS.md +3 -1
- package/template/docs/cli/README.md +34 -0
- package/template/{skills/{{binName}}/references → docs/cli}/setup.md +1 -1
- package/template/package.json +3 -1
- package/template/scripts/release.ts +0 -1
- package/template/skills/{{binName}}/SKILL.md +4 -35
- package/template/src/bin.ts +20 -2
- package/template/src/cli.ts +2 -0
- package/template/test/cli.test.ts +15 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "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.
|
|
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)
|
|
12
|
+
2. Finish with [Check](#check). {{binName}} is ready when `doctor` exits 0 with nothing in `next`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
The `skill` row
|
|
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)
|
|
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
|
-
|
|
1
|
+
Paste this to your coding agent, with your task in it:
|
|
2
2
|
|
|
3
|
-
{{
|
|
3
|
+
> Run `{{setupLine}}`, then use {{binName}} to <your task>.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
1
|
+
This installs the CLI and this skill on this machine, and is safe to run again.
|
|
2
2
|
|
|
3
|
-
1. Run `
|
|
4
|
-
2.
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
package/template/AGENTS.md
CHANGED
|
@@ -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
|
|
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 [
|
|
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
|
|
package/template/package.json
CHANGED
|
@@ -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.
|
|
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",
|
|
@@ -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
|
-
<!--
|
|
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
|
-
|
|
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
|
-
|
|
16
|
+
{{whenMissing}}
|
package/template/src/bin.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
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);
|
package/template/src/cli.ts
CHANGED
|
@@ -199,15 +199,17 @@ describe("install lines", () => {
|
|
|
199
199
|
return readFile(join(root, path), "utf8");
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
test("the setup
|
|
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}}/
|
|
207
|
-
expect(await text("
|
|
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
|
-
|
|
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
|
|
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("
|
|
224
|
-
`"allowedDomains": ["${host}"]`,
|
|
225
|
-
);
|
|
232
|
+
expect(await text("docs/cli/setup.md")).toContain(`"allowedDomains": ["${host}"]`);
|
|
226
233
|
});
|
|
227
234
|
});
|