@duyquangnvx/create-agent-cli 0.18.0 → 0.19.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 +12 -0
- package/README.md +1 -1
- package/dist/bin.js +35 -34
- package/package.json +2 -2
- package/partials/global/skillCheck.md +1 -1
- package/template/test/cli.test.ts +4 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## v0.19.0 (2026-10-28)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `defineCli({ skill })` takes `{ add: "<command line>" }`, the whole line that installs the skill from a Skill Hub whose line the Runtime cannot build. `doctor`'s `skill` fix and `update`'s `next` give it verbatim for a missing copy or one that states no version, as `npx -y skills update <name> && <line>` when another copy is older than the CLI, and `npx -y skills update <name>` for an older copy alone. `defineCli` throws on an empty line or one holding a line break.
|
|
12
|
+
- The Scaffolder's `--skill-hub-api` takes `--skill-add "<command line>"`, which it writes as `skill: { add }` and, after `! `, as the README's skill line. Either flag without the other is a usage error, `SKILL_ADD_MISSING` or `SKILL_HUB_API_MISSING`, and an empty or multi-line `--skill-add` is `SKILL_ADD_INVALID`.
|
|
13
|
+
|
|
14
|
+
### Removed
|
|
15
|
+
|
|
16
|
+
- **Breaking:** `defineCli({ skill })` no longer takes `{ page }`, and `doctor` and `update` no longer name a page; declare the hub's install line as `{ add }`.
|
|
17
|
+
- **Breaking:** the Scaffolder's `--skill-page` and its usage error `SKILL_PAGE_MISSING` are gone; pass `--skill-add`.
|
|
18
|
+
|
|
7
19
|
## v0.18.0 (2026-10-28)
|
|
8
20
|
|
|
9
21
|
### Added
|
package/README.md
CHANGED
|
@@ -6,6 +6,6 @@ Creates a new Agent CLI repository already wired to [`@duyquangnvx/agent-cli`](h
|
|
|
6
6
|
pnpm create @duyquangnvx/agent-cli <name> --description "One line saying what it does" --skill-hub-git 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` whose Agents section points Coding agents at the Runtime's docs in `node_modules`, 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-git` names the git Skill Hub each release commits that skill to and agents install it from, and `--skill-hub-api` with `--skill-
|
|
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` whose Agents section points Coding agents at the Runtime's docs in `node_modules`, 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-git` names the git Skill Hub each release commits that skill to and agents install it from, and `--skill-hub-api` with `--skill-add` an API Skill Hub and the whole line that installs the skill from it; `--dependency` instead starts a CLI each project pins as a devDependency, run as `npx -q --no-install <name>`, whose skill ships inside the package for its `skill install` to copy into the project. Pass `--dry-run` to see what it would write.
|
|
10
10
|
|
|
11
11
|
See the [repository](https://github.com/duyquangnvx/cli-for-agent) for an overview and the [Runtime's docs](https://github.com/duyquangnvx/cli-for-agent/blob/main/packages/agent-cli/docs/README.md) for how to build on it.
|
package/dist/bin.js
CHANGED
|
@@ -6764,9 +6764,9 @@ function toolRows(tools, commands) {
|
|
|
6764
6764
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
6765
6765
|
function buildStamp() {
|
|
6766
6766
|
return {
|
|
6767
|
-
"sha": "
|
|
6767
|
+
"sha": "fb29229",
|
|
6768
6768
|
"dirty": false,
|
|
6769
|
-
"builtAt": "2026-10-
|
|
6769
|
+
"builtAt": "2026-10-28T10:00:56.000Z"
|
|
6770
6770
|
};
|
|
6771
6771
|
}
|
|
6772
6772
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -7616,18 +7616,16 @@ async function skillCopies(cliName, version, { cwd, env }) {
|
|
|
7616
7616
|
return copies;
|
|
7617
7617
|
}
|
|
7618
7618
|
const has = (copies, wanted) => copies.some(({ state }) => state === wanted);
|
|
7619
|
+
function addLineOf(cliName, from) {
|
|
7620
|
+
return "add" in from ? from.add : `npx -y skills add ${from.source} --skill ${cliName} -g`;
|
|
7621
|
+
}
|
|
7619
7622
|
function fixFor(cliName, copies, from) {
|
|
7620
7623
|
const update = `npx -y skills update ${cliName}`;
|
|
7621
|
-
const add = from
|
|
7624
|
+
const add = from === void 0 ? void 0 : addLineOf(cliName, from);
|
|
7622
7625
|
if (copies.length === 0) return add;
|
|
7623
7626
|
if (has(copies, "unversioned") && add !== void 0) return has(copies, "behind") ? `${update} && ${add}` : add;
|
|
7624
|
-
if (from !== void 0 && "page" in from) return has(copies, "behind") ? update : void 0;
|
|
7625
7627
|
return has(copies, "behind") || has(copies, "unversioned") ? update : void 0;
|
|
7626
7628
|
}
|
|
7627
|
-
function pageNote(copies, from) {
|
|
7628
|
-
if (from === void 0 || !("page" in from)) return;
|
|
7629
|
-
return copies.length === 0 || has(copies, "unversioned") ? `copy your install line from ${from.page}` : void 0;
|
|
7630
|
-
}
|
|
7631
7629
|
const SKILL_STATE_NOTES = {
|
|
7632
7630
|
current: "the same as this build",
|
|
7633
7631
|
behind: "older than this build",
|
|
@@ -7638,13 +7636,11 @@ const SKILL_STATE_NOTES = {
|
|
|
7638
7636
|
function describeCopy({ path, version, state }) {
|
|
7639
7637
|
return `${path}${version === void 0 ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
|
|
7640
7638
|
}
|
|
7641
|
-
/** A skill on a Skill Hub, which `from` says how to install;
|
|
7639
|
+
/** A skill on a Skill Hub, which `from` says how to install; without it, no line adds it. */
|
|
7642
7640
|
function hubSkill(cli, from) {
|
|
7643
7641
|
const findingOf = (copies) => {
|
|
7644
7642
|
const fix = fixFor(cli.name, copies, from);
|
|
7645
|
-
const
|
|
7646
|
-
const found = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
|
|
7647
|
-
const message = note === void 0 ? found : `${found}; ${note}`;
|
|
7643
|
+
const message = copies.length === 0 ? `no copy of the ${cli.name} skill is installed` : copies.map(describeCopy).join("; ");
|
|
7648
7644
|
return fix === void 0 ? {
|
|
7649
7645
|
state: "ok",
|
|
7650
7646
|
message
|
|
@@ -7665,8 +7661,6 @@ function hubSkill(cli, from) {
|
|
|
7665
7661
|
notice: async () => {},
|
|
7666
7662
|
nextAt: async (version, ctx) => {
|
|
7667
7663
|
const copies = await skillCopies(cli.name, version, ctx);
|
|
7668
|
-
const note = pageNote(copies, from);
|
|
7669
|
-
if (note !== void 0) ctx.notice(note);
|
|
7670
7664
|
const fix = fixFor(cli.name, copies, from);
|
|
7671
7665
|
return fix === void 0 ? [] : [fix];
|
|
7672
7666
|
}
|
|
@@ -9199,13 +9193,15 @@ function checkTree(commands, path, table) {
|
|
|
9199
9193
|
else checkTree(node, at, table);
|
|
9200
9194
|
}
|
|
9201
9195
|
}
|
|
9202
|
-
function
|
|
9203
|
-
if (skill === void 0 || !("
|
|
9204
|
-
|
|
9196
|
+
function checkSkillAdd({ name, skill }) {
|
|
9197
|
+
if (skill === void 0 || !("add" in skill)) return;
|
|
9198
|
+
const declares = `${name} declares skill.add ${JSON.stringify(skill.add)}`;
|
|
9199
|
+
if (skill.add.trim() === "") throw new Error(`${declares}, which is empty`);
|
|
9200
|
+
if (/[\r\n]/.test(skill.add)) throw new Error(`${declares}, which holds a line break`);
|
|
9205
9201
|
}
|
|
9206
9202
|
function checkCli(cli) {
|
|
9207
9203
|
const table = errorTableOf(cli);
|
|
9208
|
-
|
|
9204
|
+
checkSkillAdd(cli);
|
|
9209
9205
|
if (cli.commands === void 0) {
|
|
9210
9206
|
checkCommand(cli.command, commandLabel([], cli.name).join(" "), table);
|
|
9211
9207
|
if (skillOf$1(cli, installOf(cli)).kind === "packaged") throw new Error(`${cli.name} declares a Packaged skill, which only a CLI with several Commands can install`);
|
|
@@ -11294,7 +11290,7 @@ async function runProcess(cli) {
|
|
|
11294
11290
|
//#region package.json
|
|
11295
11291
|
var package_default = {
|
|
11296
11292
|
name: "@duyquangnvx/create-agent-cli",
|
|
11297
|
-
version: "0.
|
|
11293
|
+
version: "0.19.0",
|
|
11298
11294
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11299
11295
|
type: "module",
|
|
11300
11296
|
engines: { "node": ">=22.12.0" },
|
|
@@ -11345,31 +11341,36 @@ const PARTIALS_DIR = fileURLToPath(new URL("../partials/", import.meta.url));
|
|
|
11345
11341
|
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
11346
11342
|
const COMMANDS_ANCHOR = " commands: {";
|
|
11347
11343
|
const GLOBAL_INSTALL = ` install: "global",\n`;
|
|
11348
|
-
const NO_SKILL_HUB = "<!-- Set `skill.source` in `defineCli` and `skillHub: { git }` in `scripts/release.ts` to the git URL of the Skill Hub each release commits the skill to, or set `skill.
|
|
11349
|
-
function skillHubOf({ git, api,
|
|
11344
|
+
const NO_SKILL_HUB = "<!-- Set `skill.source` in `defineCli` and `skillHub: { git }` in `scripts/release.ts` to the git URL of the Skill Hub each release commits the skill to, or set `skill.add` to the whole line that installs the skill and `skillHub: { api }` to the API Skill Hub each release publishes the skill to. Then put here what `node dist/bin.js doctor` gives in its `skill` row: the line of its fix, after `! `. -->";
|
|
11345
|
+
function skillHubOf({ git, api, add }) {
|
|
11350
11346
|
if (git !== void 0 && api !== void 0) throw new UsageError({
|
|
11351
11347
|
code: "SKILL_HOMES_CONFLICT",
|
|
11352
11348
|
message: "--skill-hub-git and --skill-hub-api each name the Skill Hub the skill goes to",
|
|
11353
11349
|
hint: "pass one of them"
|
|
11354
11350
|
});
|
|
11355
|
-
if (api !== void 0 &&
|
|
11356
|
-
code: "
|
|
11357
|
-
message: "--skill-hub-api needs --skill-
|
|
11358
|
-
hint: "add --skill-
|
|
11351
|
+
if (api !== void 0 && add === void 0) throw new UsageError({
|
|
11352
|
+
code: "SKILL_ADD_MISSING",
|
|
11353
|
+
message: "--skill-hub-api needs --skill-add, the whole line that installs the skill from the hub",
|
|
11354
|
+
hint: "add --skill-add with the hub's install line for the skill"
|
|
11359
11355
|
});
|
|
11360
|
-
if (
|
|
11356
|
+
if (add !== void 0 && api === void 0) throw new UsageError({
|
|
11361
11357
|
code: "SKILL_HUB_API_MISSING",
|
|
11362
|
-
message: "--skill-
|
|
11358
|
+
message: "--skill-add is the install line of an API Skill Hub, which --skill-hub-api names",
|
|
11363
11359
|
hint: "add --skill-hub-api with the hub's URL"
|
|
11364
11360
|
});
|
|
11361
|
+
if (add !== void 0 && (add.trim() === "" || /[\r\n]/.test(add))) throw new UsageError({
|
|
11362
|
+
code: "SKILL_ADD_INVALID",
|
|
11363
|
+
message: `--skill-add ${JSON.stringify(add)} is not one command line`,
|
|
11364
|
+
hint: "pass the hub's install line for the skill, on one line"
|
|
11365
|
+
});
|
|
11365
11366
|
if (git !== void 0) return {
|
|
11366
11367
|
kind: "git",
|
|
11367
11368
|
url: git
|
|
11368
11369
|
};
|
|
11369
|
-
return api === void 0 ||
|
|
11370
|
+
return api === void 0 || add === void 0 ? void 0 : {
|
|
11370
11371
|
kind: "api",
|
|
11371
11372
|
url: api,
|
|
11372
|
-
|
|
11373
|
+
add
|
|
11373
11374
|
};
|
|
11374
11375
|
}
|
|
11375
11376
|
function installModeOf(flags, dependency, binName) {
|
|
@@ -11391,7 +11392,7 @@ function installModeOf(flags, dependency, binName) {
|
|
|
11391
11392
|
}
|
|
11392
11393
|
function skillLineOf(skillHub, binName) {
|
|
11393
11394
|
if (skillHub === void 0) return NO_SKILL_HUB;
|
|
11394
|
-
return skillHub.kind === "git" ? `\`! npx -y skills add ${skillHub.url} --skill ${binName} -g\`` :
|
|
11395
|
+
return skillHub.kind === "git" ? `\`! npx -y skills add ${skillHub.url} --skill ${binName} -g\`` : `\`! ${skillHub.add}\``;
|
|
11395
11396
|
}
|
|
11396
11397
|
async function partialsOf(mode, values) {
|
|
11397
11398
|
const partial = async (name) => fill(await readFile(join(PARTIALS_DIR, mode, `${name}.md`), "utf8"), values, false).trimEnd();
|
|
@@ -11437,7 +11438,7 @@ async function globalSetup(skillHub, packageName, binName) {
|
|
|
11437
11438
|
};
|
|
11438
11439
|
}
|
|
11439
11440
|
function skillOf(skillHub) {
|
|
11440
|
-
return skillHub.kind === "git" ? `{ source: ${JSON.stringify(skillHub.url)} }` : `{
|
|
11441
|
+
return skillHub.kind === "git" ? `{ source: ${JSON.stringify(skillHub.url)} }` : `{ add: ${JSON.stringify(skillHub.add)} }`;
|
|
11441
11442
|
}
|
|
11442
11443
|
async function dependencySetup(packageName, binName) {
|
|
11443
11444
|
const runAs = runAsOf({
|
|
@@ -11653,15 +11654,15 @@ await runProcess(defineCli({
|
|
|
11653
11654
|
repository: url({ protocol: /^https$/ }).optional().describe("the https URL of the CLI's git repository"),
|
|
11654
11655
|
skillHubGit: 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 a git Skill Hub each release commits the CLI's skill to"),
|
|
11655
11656
|
skillHubApi: url({ protocol: /^https$/ }).optional().describe("the https URL of an API Skill Hub each release publishes the CLI's skill to"),
|
|
11656
|
-
|
|
11657
|
+
skillAdd: string().optional().describe("the whole line that installs the CLI's skill from the API Skill Hub"),
|
|
11657
11658
|
dependency: boolean().default(false).describe("install the CLI per project, as a devDependency, with its skill shipped inside the package")
|
|
11658
11659
|
}),
|
|
11659
|
-
plan: async ({ name, description, install, git, repository, skillHubGit, skillHubApi,
|
|
11660
|
+
plan: async ({ name, description, install, git, repository, skillHubGit, skillHubApi, skillAdd, dependency }, ctx) => {
|
|
11660
11661
|
const binName = PACKAGE_NAME.exec(name)?.[1] ?? name;
|
|
11661
11662
|
const mode = installModeOf({
|
|
11662
11663
|
git: skillHubGit,
|
|
11663
11664
|
api: skillHubApi,
|
|
11664
|
-
|
|
11665
|
+
add: skillAdd
|
|
11665
11666
|
}, dependency, binName);
|
|
11666
11667
|
const path = resolve(ctx.cwd, binName);
|
|
11667
11668
|
await ensureFree(path);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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.19.0",
|
|
25
25
|
"tsdown": "0.23.0",
|
|
26
26
|
"zod": "4.6.7"
|
|
27
27
|
},
|
|
@@ -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
|
|
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.
|
|
@@ -207,15 +207,13 @@ describe("install lines", () => {
|
|
|
207
207
|
expect(await text("README.md")).toContain(`\`! ${install}\``);
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
-
// A hub that gives each user their own install line has no fix: the row's message names its page.
|
|
211
210
|
test.skipIf(cli.skill === undefined)(
|
|
212
|
-
"the README installs the skill
|
|
211
|
+
"the README installs the skill with doctor's skill line",
|
|
213
212
|
async () => {
|
|
214
|
-
const
|
|
215
|
-
const line = fix === undefined ? message.split("; ").at(-1) : `\`! ${fix}\``;
|
|
213
|
+
const fix = (await doctorRows()).skill?.fix;
|
|
216
214
|
|
|
217
|
-
expect(
|
|
218
|
-
expect(await text("README.md")).toContain(
|
|
215
|
+
expect(fix).toBeDefined();
|
|
216
|
+
expect(await text("README.md")).toContain(`\`! ${fix}\``);
|
|
219
217
|
},
|
|
220
218
|
);
|
|
221
219
|
|