@duyquangnvx/create-agent-cli 0.6.0 → 0.8.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 +99 -49
- package/package.json +2 -2
- package/template/CHANGELOG.md +1 -1
- package/template/skills/{{binName}}/references/setup.md +1 -1
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.8.0 (2026-10-20)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **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>:`.
|
|
12
|
+
- **Breaking:** each entry of `upgrades` in `update --json` is `{ version, steps }` or `{ version, notes }`.
|
|
13
|
+
- 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.
|
|
14
|
+
|
|
15
|
+
## v0.7.0 (2026-10-20)
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Every Runtime code is exported under its name: `OUTPUT_EXISTS`, `PROJECT_INVALID`, `INVALID_INPUT`, `UNKNOWN_OPTION`, `UNKNOWN_COMMAND`, `UNKNOWN_FIELD`, `CONFIRMATION_REQUIRED`, `CONFIRMATION_MISMATCH`, `NOT_CONFIRMED`, `TOOL_FAILED`, `TOOL_MISSING`, `DOCTOR_PROBLEMS`, `INTERNAL`, `NOT_GLOBAL_INSTALL`, `REGISTRY_UNREACHABLE`, `REGISTRY_AUTH`, `INSTALL_FAILED`, `NPM_FAILED` and `NO_LATEST`. The agent-cli skill says which of them a Command may throw, and asserts codes through them.
|
|
20
|
+
- A CLI's `doctor` rows read the Project file as `ctx.project`, which was always `undefined` there. `doctor` reads the file once for all its rows; a row that reads `ctx.project` while the file is unusable is skipped, since the `project` row reports it. See `DoctorCheck.run`. The agent-cli skill's example adds a `tag` row that reads it.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Breaking:** `doctor`'s `project` row carries the code of the failed read, so a Project file its schema rejects fails `doctor` with `PROJECT_INVALID` instead of `DOCTOR_PROBLEMS` (still exit 2), the code a Command fails with on the same file. The row's message adds the error's hint.
|
|
25
|
+
|
|
7
26
|
## v0.6.0 (2026-10-20)
|
|
8
27
|
|
|
9
28
|
### Changed
|
package/dist/bin.js
CHANGED
|
@@ -5795,7 +5795,7 @@ var UsageError = class extends CliError {
|
|
|
5795
5795
|
this.name = "UsageError";
|
|
5796
5796
|
}
|
|
5797
5797
|
};
|
|
5798
|
-
const
|
|
5798
|
+
const INTERNAL = "INTERNAL";
|
|
5799
5799
|
function errnoOf(error) {
|
|
5800
5800
|
return error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
5801
5801
|
}
|
|
@@ -5835,7 +5835,7 @@ function fieldsOf$1(error, format) {
|
|
|
5835
5835
|
};
|
|
5836
5836
|
}
|
|
5837
5837
|
return {
|
|
5838
|
-
code:
|
|
5838
|
+
code: INTERNAL,
|
|
5839
5839
|
message: messageOf(error),
|
|
5840
5840
|
hint: bugHint(format)
|
|
5841
5841
|
};
|
|
@@ -7190,22 +7190,41 @@ function nodeRow(cli) {
|
|
|
7190
7190
|
}
|
|
7191
7191
|
};
|
|
7192
7192
|
}
|
|
7193
|
-
|
|
7194
|
-
|
|
7193
|
+
/** A row's thrown error as its problem: a `CliError` with its hint and code, anything else a bug. */
|
|
7194
|
+
function problemOfError(name, error) {
|
|
7195
|
+
if (error instanceof CliError) return {
|
|
7196
|
+
state: "problem",
|
|
7197
|
+
message: `${error.message}; ${error.hint}`,
|
|
7198
|
+
code: error.code
|
|
7199
|
+
};
|
|
7200
|
+
return {
|
|
7201
|
+
state: "problem",
|
|
7202
|
+
message: `the ${name} row hit a bug: ${messageOf(error)}`
|
|
7203
|
+
};
|
|
7204
|
+
}
|
|
7205
|
+
function readProject(projectFile, ctx) {
|
|
7206
|
+
if (projectFile === void 0) return {
|
|
7207
|
+
ok: true,
|
|
7208
|
+
project: void 0
|
|
7209
|
+
};
|
|
7210
|
+
try {
|
|
7211
|
+
return {
|
|
7212
|
+
ok: true,
|
|
7213
|
+
project: projectOf(projectFile, ctx.cwd, ctx.fileError)
|
|
7214
|
+
};
|
|
7215
|
+
} catch (error) {
|
|
7216
|
+
return {
|
|
7217
|
+
ok: false,
|
|
7218
|
+
error
|
|
7219
|
+
};
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7222
|
+
function projectRow({ file }, read) {
|
|
7195
7223
|
return {
|
|
7196
7224
|
name: "project",
|
|
7197
7225
|
run: (ctx) => {
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
project = projectOf(projectFile, ctx.cwd, ctx.fileError);
|
|
7201
|
-
} catch (error) {
|
|
7202
|
-
if (error instanceof CliError && error.code === "PROJECT_INVALID") return {
|
|
7203
|
-
state: "problem",
|
|
7204
|
-
message: error.message
|
|
7205
|
-
};
|
|
7206
|
-
throw error;
|
|
7207
|
-
}
|
|
7208
|
-
return ok(project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(project.root, file));
|
|
7226
|
+
if (!read.ok) return problemOfError("project", read.error);
|
|
7227
|
+
return ok(read.project === void 0 ? `none found: no ${file} at or above ${ctx.cwd}` : join(read.project.root, file));
|
|
7209
7228
|
}
|
|
7210
7229
|
};
|
|
7211
7230
|
}
|
|
@@ -7273,11 +7292,11 @@ function skillRow(cli) {
|
|
|
7273
7292
|
};
|
|
7274
7293
|
}
|
|
7275
7294
|
/** Every row `doctor` reports, in order: the fixed rows around the CLI's own and its tools'. */
|
|
7276
|
-
function doctorChecks(cli, launch) {
|
|
7295
|
+
function doctorChecks(cli, launch, read) {
|
|
7277
7296
|
return [
|
|
7278
7297
|
buildRow(cli),
|
|
7279
7298
|
nodeRow(cli),
|
|
7280
|
-
...cli.project === void 0 ? [] : [projectRow(cli.project)],
|
|
7299
|
+
...cli.project === void 0 ? [] : [projectRow(cli.project, read)],
|
|
7281
7300
|
...cli.doctor ?? [],
|
|
7282
7301
|
...toolRows(cli.tools ?? [], cli.commands),
|
|
7283
7302
|
installRow(cli, launch),
|
|
@@ -7310,28 +7329,29 @@ function problemOf(check, error, { signal, deadline, ms }) {
|
|
|
7310
7329
|
state: "problem",
|
|
7311
7330
|
message: `${check.name} did not answer within ${ms / MS_PER_SECOND} s`
|
|
7312
7331
|
};
|
|
7313
|
-
|
|
7314
|
-
state: "problem",
|
|
7315
|
-
message: `${error.message}; ${error.hint}`,
|
|
7316
|
-
code: error.code
|
|
7317
|
-
};
|
|
7318
|
-
return {
|
|
7319
|
-
state: "problem",
|
|
7320
|
-
message: `the ${check.name} row hit a bug: ${messageOf(error)}`
|
|
7321
|
-
};
|
|
7332
|
+
return problemOfError(check.name, error);
|
|
7322
7333
|
}
|
|
7323
|
-
function
|
|
7324
|
-
return Object.create(ctx, {
|
|
7334
|
+
function rowContext(ctx, signal, read) {
|
|
7335
|
+
return Object.create(ctx, {
|
|
7336
|
+
signal: { value: signal },
|
|
7337
|
+
project: { get: () => {
|
|
7338
|
+
if (!read.ok) throw read.error;
|
|
7339
|
+
return read.project;
|
|
7340
|
+
} }
|
|
7341
|
+
});
|
|
7325
7342
|
}
|
|
7326
|
-
async function rowOf(check, ctx) {
|
|
7343
|
+
async function rowOf(check, ctx, read) {
|
|
7327
7344
|
const ms = check.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
7328
7345
|
const signal = ctx.deadline(ms);
|
|
7329
7346
|
let finding;
|
|
7330
7347
|
try {
|
|
7331
|
-
finding = await Promise.race([Promise.resolve().then(() => check.run(
|
|
7348
|
+
finding = await Promise.race([Promise.resolve().then(() => check.run(rowContext(ctx, signal, read))), untilAborted(signal)]);
|
|
7332
7349
|
} catch (error) {
|
|
7333
7350
|
if (ctx.signal.aborted) return void 0;
|
|
7334
|
-
finding =
|
|
7351
|
+
finding = !read.ok && error === read.error ? {
|
|
7352
|
+
state: "skipped",
|
|
7353
|
+
message: "the Project file is unusable; the project row says why"
|
|
7354
|
+
} : problemOf(check, error, {
|
|
7335
7355
|
signal: ctx.signal,
|
|
7336
7356
|
deadline: signal,
|
|
7337
7357
|
ms
|
|
@@ -7379,7 +7399,8 @@ function doctorCommand(cli, launch) {
|
|
|
7379
7399
|
effect: "read",
|
|
7380
7400
|
output: reportSchema,
|
|
7381
7401
|
run: async (_input, ctx) => {
|
|
7382
|
-
const
|
|
7402
|
+
const read = readProject(cli.project, ctx);
|
|
7403
|
+
const checked = (await Promise.all(doctorChecks(cli, launch, read).map((check) => rowOf(check, ctx, read)))).filter((row) => row !== void 0);
|
|
7383
7404
|
const rows = checked.map(({ row }) => row);
|
|
7384
7405
|
const report = {
|
|
7385
7406
|
rows,
|
|
@@ -7533,8 +7554,20 @@ var RunControl = class {
|
|
|
7533
7554
|
}
|
|
7534
7555
|
};
|
|
7535
7556
|
//#endregion
|
|
7536
|
-
//#region ../agent-cli/dist/built-ins/
|
|
7557
|
+
//#region ../agent-cli/dist/built-ins/upgrades.js
|
|
7558
|
+
/** A skipped release's Upgrade steps, or its whole section when it lists none. */
|
|
7559
|
+
const upgradeSchema = union([object({
|
|
7560
|
+
version: string(),
|
|
7561
|
+
steps: array(string())
|
|
7562
|
+
}), object({
|
|
7563
|
+
version: string(),
|
|
7564
|
+
notes: string()
|
|
7565
|
+
})]);
|
|
7537
7566
|
const RELEASE_HEADING = /^## v?(\S+)/;
|
|
7567
|
+
const UPGRADE_HEADING = /^### Upgrade from v\S+$/;
|
|
7568
|
+
const HEADING = /^#{1,6} /;
|
|
7569
|
+
const ITEM = /^[-*] /;
|
|
7570
|
+
const NOTHING_TO_DO = /^nothing to do\.?$/i;
|
|
7538
7571
|
function releasesIn(changelog) {
|
|
7539
7572
|
const releases = [];
|
|
7540
7573
|
let current;
|
|
@@ -7549,16 +7582,33 @@ function releasesIn(changelog) {
|
|
|
7549
7582
|
if (current !== void 0) releases.push(current);
|
|
7550
7583
|
} else current?.lines.push(line);
|
|
7551
7584
|
}
|
|
7552
|
-
return releases
|
|
7585
|
+
return releases;
|
|
7586
|
+
}
|
|
7587
|
+
function listedSteps(lines) {
|
|
7588
|
+
const start = lines.findIndex((line) => UPGRADE_HEADING.test(line.trimEnd()));
|
|
7589
|
+
if (start === -1) return [];
|
|
7590
|
+
const end = lines.findIndex((line, i) => i > start && HEADING.test(line));
|
|
7591
|
+
const body = lines.slice(start + 1, end === -1 ? void 0 : end).filter((line) => line.trim() !== "");
|
|
7592
|
+
const starts = body.flatMap((line, i) => ITEM.test(line) ? [i] : []);
|
|
7593
|
+
return starts.map((first, i) => body.slice(first, starts[i + 1]).map((line) => line.trim()).join(" ").replace(ITEM, ""));
|
|
7594
|
+
}
|
|
7595
|
+
function upgradeOf({ version, lines }) {
|
|
7596
|
+
const listed = listedSteps(lines);
|
|
7597
|
+
if (listed.length === 0) return [{
|
|
7553
7598
|
version,
|
|
7554
7599
|
notes: lines.join("\n").trim()
|
|
7555
|
-
}
|
|
7600
|
+
}];
|
|
7601
|
+
const todo = listed.filter((step) => !NOTHING_TO_DO.test(step));
|
|
7602
|
+
return todo.length === 0 ? [] : [{
|
|
7603
|
+
version,
|
|
7604
|
+
steps: todo
|
|
7605
|
+
}];
|
|
7556
7606
|
}
|
|
7557
7607
|
/**
|
|
7558
|
-
* What
|
|
7559
|
-
* `packageDir`; none when the package ships no CHANGELOG.
|
|
7608
|
+
* What each release after `from` up to `to` asks of the operator, oldest first, from the
|
|
7609
|
+
* CHANGELOG.md in `packageDir`; none when the package ships no CHANGELOG.
|
|
7560
7610
|
*/
|
|
7561
|
-
async function
|
|
7611
|
+
async function upgradesOf(packageDir, from, to) {
|
|
7562
7612
|
let changelog;
|
|
7563
7613
|
try {
|
|
7564
7614
|
changelog = await readFile(join(packageDir, "CHANGELOG.md"), "utf8");
|
|
@@ -7566,7 +7616,7 @@ async function upgradeNotes(packageDir, from, to) {
|
|
|
7566
7616
|
if (isMissingFile(error)) return [];
|
|
7567
7617
|
throw error;
|
|
7568
7618
|
}
|
|
7569
|
-
return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version));
|
|
7619
|
+
return releasesIn(changelog).filter(({ version }) => compareVersions(version, from) > 0 && compareVersions(version, to) <= 0).sort((a, b) => compareVersions(a.version, b.version)).flatMap(upgradeOf);
|
|
7570
7620
|
}
|
|
7571
7621
|
//#endregion
|
|
7572
7622
|
//#region ../agent-cli/dist/built-ins/update.js
|
|
@@ -7577,18 +7627,18 @@ const updateSchema = object({
|
|
|
7577
7627
|
version: string(),
|
|
7578
7628
|
prefix: string()
|
|
7579
7629
|
}).nullable(),
|
|
7580
|
-
upgrades: array(
|
|
7581
|
-
version: string(),
|
|
7582
|
-
notes: string()
|
|
7583
|
-
})),
|
|
7630
|
+
upgrades: array(upgradeSchema),
|
|
7584
7631
|
next: array(string())
|
|
7585
7632
|
});
|
|
7586
7633
|
function nothingToInstall({ from, latest }) {
|
|
7587
7634
|
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`;
|
|
7588
7635
|
}
|
|
7636
|
+
function renderUpgrade(upgrade) {
|
|
7637
|
+
return "steps" in upgrade ? [`\nupgrade steps for ${upgrade.version}:`, ...upgrade.steps.map((step) => `- ${step}`)].join("\n") : `\nchanges in ${upgrade.version}:\n${upgrade.notes}`;
|
|
7638
|
+
}
|
|
7589
7639
|
function renderUpdate(cli, install, update) {
|
|
7590
7640
|
const { installed } = update;
|
|
7591
|
-
const head = installed === null ? [nothingToInstall(update)] : [`installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`, ...update.upgrades.map(
|
|
7641
|
+
const head = installed === null ? [nothingToInstall(update)] : [`installed ${install.package}@${installed.version} with the npm under ${installed.prefix}, replacing ${update.from}`, ...update.upgrades.map(renderUpgrade)];
|
|
7592
7642
|
const next = update.next.map((command) => `next: ${command}, so the ${cli.name} skill describes ${update.latest}`);
|
|
7593
7643
|
return (next.length === 0 ? head : [
|
|
7594
7644
|
...head,
|
|
@@ -7651,7 +7701,7 @@ function updateCommand(cli, install, { bin, node }) {
|
|
|
7651
7701
|
version: latest,
|
|
7652
7702
|
prefix
|
|
7653
7703
|
},
|
|
7654
|
-
upgrades: await
|
|
7704
|
+
upgrades: await upgradesOf(plan.packageDir, from, latest),
|
|
7655
7705
|
next: await skillNext(cli, latest, ctx)
|
|
7656
7706
|
};
|
|
7657
7707
|
if (ctx.signal.aborted) {
|
|
@@ -8075,7 +8125,7 @@ const RUNTIME_EXIT_CLASSES = {
|
|
|
8075
8125
|
[STDIN_STALLED]: "caller",
|
|
8076
8126
|
[INTERRUPTED]: "interrupted",
|
|
8077
8127
|
[DEADLINE_EXCEEDED]: "wait",
|
|
8078
|
-
[
|
|
8128
|
+
[INTERNAL]: "bug",
|
|
8079
8129
|
[PERMISSION_DENIED]: "operator",
|
|
8080
8130
|
[DISK_FULL]: "operator",
|
|
8081
8131
|
[NOT_FOUND]: "caller",
|
|
@@ -9387,14 +9437,14 @@ async function runProcess(cli) {
|
|
|
9387
9437
|
}
|
|
9388
9438
|
//#endregion
|
|
9389
9439
|
//#region package.json
|
|
9390
|
-
var version$1 = "0.
|
|
9440
|
+
var version$1 = "0.8.0";
|
|
9391
9441
|
var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
|
|
9392
9442
|
//#endregion
|
|
9393
9443
|
//#region src/build.ts
|
|
9394
9444
|
const build = {
|
|
9395
|
-
"sha": "
|
|
9445
|
+
"sha": "d2cc610",
|
|
9396
9446
|
"dirty": false,
|
|
9397
|
-
"builtAt": "2026-10-
|
|
9447
|
+
"builtAt": "2026-10-20T09:33:37.544Z"
|
|
9398
9448
|
};
|
|
9399
9449
|
//#endregion
|
|
9400
9450
|
//#region src/steps.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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.
|
|
22
|
+
"@duyquangnvx/agent-cli": "0.8.0",
|
|
23
23
|
"tsdown": "0.23.0",
|
|
24
24
|
"zod": "4.6.7"
|
|
25
25
|
},
|
package/template/CHANGELOG.md
CHANGED
|
@@ -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
|
|
@@ -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
|
|
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
|
|