@duyquangnvx/create-agent-cli 0.28.0 → 0.29.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 +19 -0
- package/dist/bin.js +25 -19
- package/package.json +2 -2
- package/template/biome.json +1 -1
- package/template/package.json +2 -2
- package/template/src/cli.ts +2 -4
- package/template/test/cli.test.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## v0.29.0 (2026-11-09)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `agentCli` returns `defineDoctorCheck`, which types a `doctor` row's `ctx` by the CLI's ports and Project file and its `errors` by the Error table, so a row needs no `DoctorCheck<Ports, Settings>` annotation and a code the table lacks is a type error.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **Breaking:** `defineCommand`, as `agentCli` returns it, is one method per Effect: `defineCommand.read`, `.act`, `.write`, `.destructive`, `.serve`, and `.list` for a List Command. A Command no longer declares `effect`, which the method sets, and `defineCommand.read` no longer takes `rows`. Each method has one signature, so a type error names the property at fault, such as a `run` returning a string where `output` has a number, rather than an Effect. The scaffolded template uses the methods.
|
|
16
|
+
- **Breaking:** `checkExamples` from `@duyquangnvx/agent-cli/testing` is `checkCli`, since it checks more than examples; the scaffolded template's test calls it.
|
|
17
|
+
- **Breaking:** `checkCli` also fails on a Command help shows that has no example, and on an arg or option without `.describe()`, so a CLI's test that passed `checkExamples` may fail until it adds them.
|
|
18
|
+
- `ctx.secret` with a name the Command does not declare is a type error that names the declared Secrets, or reads `"the Command declares no secrets"` when it declares none, rather than one about `never`.
|
|
19
|
+
- The scaffolded template pins `@biomejs/biome` 2.6.2 and `@duyquangnvx/harness` 0.5.2, the versions the Runtime itself is checked with.
|
|
20
|
+
|
|
21
|
+
### Upgrade from v0.28.0
|
|
22
|
+
|
|
23
|
+
- Call `checkCli` where the CLI's tests called `checkExamples`, then give each Command and field it names an example or a `.describe()`.
|
|
24
|
+
- Declare each Command with the method of its Effect and delete its `effect`: `defineCommand({ effect: "write", … })` becomes `defineCommand.write({ … })`, and a read Command that declares `rows` becomes `defineCommand.list({ … })`.
|
|
25
|
+
|
|
7
26
|
## v0.28.0 (2026-11-09)
|
|
8
27
|
|
|
9
28
|
### Added
|
package/dist/bin.js
CHANGED
|
@@ -7700,10 +7700,20 @@ function checkErrors(errors, label, table) {
|
|
|
7700
7700
|
if (unknown.length > 0) throw new Error(`${label} declares ${unknown.join(", ")} in errors, a code the Error table lacks`);
|
|
7701
7701
|
}
|
|
7702
7702
|
function defineCommandFor() {
|
|
7703
|
-
return
|
|
7703
|
+
return {
|
|
7704
|
+
list: withEffect("read"),
|
|
7705
|
+
read: withEffect("read"),
|
|
7706
|
+
act: withEffect("act"),
|
|
7707
|
+
serve: withEffect("serve"),
|
|
7708
|
+
write: withEffect("write"),
|
|
7709
|
+
destructive: withEffect("destructive")
|
|
7710
|
+
};
|
|
7704
7711
|
}
|
|
7705
|
-
function
|
|
7706
|
-
return command
|
|
7712
|
+
function withEffect(effect) {
|
|
7713
|
+
return (command) => ({
|
|
7714
|
+
...command,
|
|
7715
|
+
effect
|
|
7716
|
+
});
|
|
7707
7717
|
}
|
|
7708
7718
|
const defineCommand$1 = defineCommandFor();
|
|
7709
7719
|
//#endregion
|
|
@@ -7724,9 +7734,9 @@ function cliErrorOf(code, init, options) {
|
|
|
7724
7734
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7725
7735
|
function buildStamp() {
|
|
7726
7736
|
return {
|
|
7727
|
-
"sha": "
|
|
7737
|
+
"sha": "0478338",
|
|
7728
7738
|
"dirty": false,
|
|
7729
|
-
"builtAt": "2026-11-
|
|
7739
|
+
"builtAt": "2026-11-09T05:16:34.000Z"
|
|
7730
7740
|
};
|
|
7731
7741
|
}
|
|
7732
7742
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -8604,7 +8614,7 @@ function renderSetup(cli, install, setup) {
|
|
|
8604
8614
|
}
|
|
8605
8615
|
/** The built-in `setup`, which runs from any copy, such as npx's, so it never asks to be the global one. */
|
|
8606
8616
|
function setupCommand(cli, install, launch) {
|
|
8607
|
-
return defineCommand$1({
|
|
8617
|
+
return defineCommand$1.write({
|
|
8608
8618
|
description: "install this CLI and its skill on this machine",
|
|
8609
8619
|
examples: [{
|
|
8610
8620
|
command: `${cli.name} setup`,
|
|
@@ -8613,7 +8623,6 @@ function setupCommand(cli, install, launch) {
|
|
|
8613
8623
|
command: `${cli.name} setup --dry-run`,
|
|
8614
8624
|
note: "show what it would change"
|
|
8615
8625
|
}],
|
|
8616
|
-
effect: "write",
|
|
8617
8626
|
errors: [
|
|
8618
8627
|
NOT_GLOBAL_INSTALL,
|
|
8619
8628
|
NOT_ON_PATH,
|
|
@@ -8705,7 +8714,7 @@ function interruptedAfterInstall(plan) {
|
|
|
8705
8714
|
}
|
|
8706
8715
|
/** The built-in `update`, for a CLI installed globally with npm. */
|
|
8707
8716
|
function updateCommand(cli, install, launch) {
|
|
8708
|
-
return defineCommand$1({
|
|
8717
|
+
return defineCommand$1.write({
|
|
8709
8718
|
description: "install the latest version of this CLI from its registry",
|
|
8710
8719
|
examples: [{
|
|
8711
8720
|
command: `${cli.name} update`,
|
|
@@ -8714,7 +8723,6 @@ function updateCommand(cli, install, launch) {
|
|
|
8714
8723
|
command: `${cli.name} update --dry-run`,
|
|
8715
8724
|
note: "show the version it would install"
|
|
8716
8725
|
}],
|
|
8717
|
-
effect: "write",
|
|
8718
8726
|
errors: [NOT_GLOBAL_INSTALL, ...NPM_ERRORS],
|
|
8719
8727
|
output: updateSchema,
|
|
8720
8728
|
plan: async (_input, ctx) => {
|
|
@@ -9077,7 +9085,7 @@ function renderInstalled(cli, data) {
|
|
|
9077
9085
|
}
|
|
9078
9086
|
/** The built-in `skill install`, for a CLI whose skill ships in its package. */
|
|
9079
9087
|
function skillInstallCommand(cli, folder, agents) {
|
|
9080
|
-
return defineCommand$1({
|
|
9088
|
+
return defineCommand$1.write({
|
|
9081
9089
|
description: agents === void 0 ? "copy this CLI's skill into the project" : "copy this CLI's skill into the project and write its Agents section",
|
|
9082
9090
|
examples: [{
|
|
9083
9091
|
command: `${cli.name} skill install`,
|
|
@@ -9086,7 +9094,6 @@ function skillInstallCommand(cli, folder, agents) {
|
|
|
9086
9094
|
command: `${cli.name} skill install --dry-run`,
|
|
9087
9095
|
note: "show what it would change"
|
|
9088
9096
|
}],
|
|
9089
|
-
effect: "write",
|
|
9090
9097
|
output: installSchema,
|
|
9091
9098
|
plan: (_input, ctx) => planOf(cli, folder, agents, ctx),
|
|
9092
9099
|
apply: async (plan, ctx) => {
|
|
@@ -9428,7 +9435,7 @@ function checkedRows(error, problems, table) {
|
|
|
9428
9435
|
}
|
|
9429
9436
|
/** The built-in `doctor`: the fixed rows around the CLI's own, each under its own deadline. */
|
|
9430
9437
|
function doctorCommand(cli, launch) {
|
|
9431
|
-
const doctor = defineCommand$1({
|
|
9438
|
+
const doctor = defineCommand$1.read({
|
|
9432
9439
|
description: "check the setup and say what fixes each problem",
|
|
9433
9440
|
examples: [{
|
|
9434
9441
|
command: `${cli.name} doctor`,
|
|
@@ -9437,7 +9444,6 @@ function doctorCommand(cli, launch) {
|
|
|
9437
9444
|
command: `${cli.name} doctor --json`,
|
|
9438
9445
|
note: "get the rows and fixes as data"
|
|
9439
9446
|
}],
|
|
9440
|
-
effect: "read",
|
|
9441
9447
|
output: reportSchema,
|
|
9442
9448
|
run: async (_input, ctx) => {
|
|
9443
9449
|
const rowsScope = {
|
|
@@ -9634,7 +9640,7 @@ function checkDocs({ name, docs }) {
|
|
|
9634
9640
|
if (docs === void 0) return;
|
|
9635
9641
|
if (!(docs !== "" && !isAbsolute(docs) && !normalize(docs).startsWith(".."))) throw new Error(`${name} declares docs ${JSON.stringify(docs)}: name a folder inside its package, relative to the package root`);
|
|
9636
9642
|
}
|
|
9637
|
-
function
|
|
9643
|
+
function checkDeclared(cli) {
|
|
9638
9644
|
const table = errorTableOf(cli);
|
|
9639
9645
|
checkDocs(cli);
|
|
9640
9646
|
if (cli.commands === void 0) {
|
|
@@ -9707,16 +9713,17 @@ function resolved(cli) {
|
|
|
9707
9713
|
}
|
|
9708
9714
|
/**
|
|
9709
9715
|
* Takes the CLI's ports factory, Project file and Error table once; the `defineCommand`,
|
|
9710
|
-
* `defineCli` and `cliError` it returns are typed by them.
|
|
9716
|
+
* `defineDoctorCheck`, `defineCli` and `cliError` it returns are typed by them.
|
|
9711
9717
|
*/
|
|
9712
9718
|
function agentCli(values = {}) {
|
|
9713
9719
|
checkOwnCodes(errorTableOf(values));
|
|
9714
9720
|
return {
|
|
9715
9721
|
defineCommand: defineCommandFor(),
|
|
9716
|
-
defineCli: (cli) =>
|
|
9722
|
+
defineCli: (cli) => checkDeclared({
|
|
9717
9723
|
...resolved(cli),
|
|
9718
9724
|
...values
|
|
9719
9725
|
}),
|
|
9726
|
+
defineDoctorCheck: (row) => row,
|
|
9720
9727
|
cliError: cliErrorOf
|
|
9721
9728
|
};
|
|
9722
9729
|
}
|
|
@@ -11744,7 +11751,7 @@ async function runProcess(cli) {
|
|
|
11744
11751
|
//#region package.json
|
|
11745
11752
|
var package_default = {
|
|
11746
11753
|
name: "@duyquangnvx/create-agent-cli",
|
|
11747
|
-
version: "0.
|
|
11754
|
+
version: "0.29.0",
|
|
11748
11755
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11749
11756
|
type: "module",
|
|
11750
11757
|
engines: { "node": ">=22.12.0" },
|
|
@@ -12031,13 +12038,12 @@ async function ensureFree(path) {
|
|
|
12031
12038
|
//#region src/bin.ts
|
|
12032
12039
|
await runProcess(defineCli({
|
|
12033
12040
|
manifest: package_default,
|
|
12034
|
-
command: defineCommand({
|
|
12041
|
+
command: defineCommand.write({
|
|
12035
12042
|
description: "Create a new Agent CLI project in ./<name>",
|
|
12036
12043
|
examples: [{
|
|
12037
12044
|
command: "create-agent-cli weather --description \"Weather forecasts\"",
|
|
12038
12045
|
note: "create ./weather, install dependencies and make the first commit"
|
|
12039
12046
|
}],
|
|
12040
|
-
effect: "write",
|
|
12041
12047
|
errors: ["STEP_FAILED"],
|
|
12042
12048
|
args: object({ name: string().regex(PACKAGE_NAME, "must be a lowercase npm package name, such as weather or @me/weather").describe("package name; its unscoped part names the bin and the directory") }),
|
|
12043
12049
|
options: object({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.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.29.0",
|
|
25
25
|
"tsdown": "0.23.0",
|
|
26
26
|
"zod": "4.6.7"
|
|
27
27
|
},
|
package/template/biome.json
CHANGED
package/template/package.json
CHANGED
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"release": "node scripts/release.ts"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@biomejs/biome": "2.
|
|
31
|
+
"@biomejs/biome": "2.6.2",
|
|
32
32
|
"@duyquangnvx/agent-cli": "{{runtimeVersion}}",
|
|
33
|
-
"@duyquangnvx/harness": "0.
|
|
33
|
+
"@duyquangnvx/harness": "0.5.2",
|
|
34
34
|
"@duyquangnvx/release": "0.9.0",
|
|
35
35
|
"@types/node": "^22.19.0",
|
|
36
36
|
"tsdown": "0.23.0",
|
package/template/src/cli.ts
CHANGED
|
@@ -9,7 +9,7 @@ const { defineCli, defineCommand } = agentCli({ errors, ports });
|
|
|
9
9
|
const FREE_LANGUAGE = "en";
|
|
10
10
|
const PRICE_PER_CARD = "$0.01";
|
|
11
11
|
|
|
12
|
-
const greet = defineCommand({
|
|
12
|
+
const greet = defineCommand.read({
|
|
13
13
|
description: "Greet someone by name",
|
|
14
14
|
examples: [
|
|
15
15
|
{
|
|
@@ -21,14 +21,13 @@ const greet = defineCommand({
|
|
|
21
21
|
note: "get the greeting as JSON",
|
|
22
22
|
},
|
|
23
23
|
],
|
|
24
|
-
effect: "read",
|
|
25
24
|
args: z.object({
|
|
26
25
|
name: z.string().min(1).describe("who to greet"),
|
|
27
26
|
}),
|
|
28
27
|
run: ({ name }) => ({ greeting: `Hello, ${name}!` }),
|
|
29
28
|
});
|
|
30
29
|
|
|
31
|
-
const card = defineCommand({
|
|
30
|
+
const card = defineCommand.write({
|
|
32
31
|
description: "Write a greeting card to <name>.txt",
|
|
33
32
|
help:
|
|
34
33
|
`A card in English is free; one in another language costs ${PRICE_PER_CARD} and needs --yes. ` +
|
|
@@ -47,7 +46,6 @@ const card = defineCommand({
|
|
|
47
46
|
note: "sign the card from Ben and Cy",
|
|
48
47
|
},
|
|
49
48
|
],
|
|
50
|
-
effect: "write",
|
|
51
49
|
errors: ["LANGUAGE_UNSUPPORTED"],
|
|
52
50
|
args: z.object({
|
|
53
51
|
name: z
|
|
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { type Files, runCli } from "@duyquangnvx/agent-cli";
|
|
6
|
-
import {
|
|
6
|
+
import { checkCli } from "@duyquangnvx/agent-cli/testing";
|
|
7
7
|
import { describe, expect, test } from "vitest";
|
|
8
8
|
import manifest from "../package.json" with { type: "json" };
|
|
9
9
|
import { cli } from "../src/cli.ts";
|
|
@@ -74,8 +74,8 @@ function run(
|
|
|
74
74
|
return runCli(cli, argv, { cwd: CWD, files, ports: { greetings }, stdin });
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
test("every example
|
|
78
|
-
await
|
|
77
|
+
test("every example runs, every Command help shows has one, and every arg and option is described", async () => {
|
|
78
|
+
await checkCli(cli);
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
describe("greet", () => {
|