@duyquangnvx/create-agent-cli 0.18.0 → 0.20.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 +23 -0
- package/README.md +1 -1
- package/dist/bin.js +41 -40
- package/package.json +2 -2
- package/partials/global/skillCheck.md +1 -1
- package/template/skills/{{binName}}/SKILL.md +5 -13
- package/template/test/cli.test.ts +4 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## v0.20.0 (2026-10-28)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Breaking:** the hints of `OUTPUT_EXISTS` and `ALREADY_EXISTS` read `once the user agrees to replace it, run the Next step` (`them` for several, and `remove` for `skill install` removing an edited file), `NOT_CONFIRMED`'s reads `once the user approves the cost, run the Next step`, and those of `CONFIRMATION_REQUIRED` and `CONFIRMATION_MISMATCH` read `once the user confirms the target, run the Next step`, so an agent asks before adding `--force`, `--yes` or `--confirm` without a skill telling it to. The docs' example `bundle` gives `BUNDLE_EXISTS` the same kind of hint.
|
|
12
|
+
- **Breaking:** the scaffolded skill's Recovering from errors section tells the agent to do what the hint says, and keeps a row only for `LANGUAGE_UNSUPPORTED`, which now asks the user whether an English card will do before running the Next step. The rows for `OUTPUT_EXISTS`, `NOT_CONFIRMED` and every exit code are gone, since the hints and `--help` say the same.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- `--force` in help reads `replace a file the run writes that already exists; --dry-run lists every file it writes`, since `--dry-run` lists the files a run writes, not which of them exist.
|
|
17
|
+
|
|
18
|
+
## v0.19.0 (2026-10-28)
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `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.
|
|
23
|
+
- 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`.
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- **Breaking:** `defineCli({ skill })` no longer takes `{ page }`, and `doctor` and `update` no longer name a page; declare the hub's install line as `{ add }`.
|
|
28
|
+
- **Breaking:** the Scaffolder's `--skill-page` and its usage error `SKILL_PAGE_MISSING` are gone; pass `--skill-add`.
|
|
29
|
+
|
|
7
30
|
## v0.18.0 (2026-10-28)
|
|
8
31
|
|
|
9
32
|
### 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
|
@@ -6471,7 +6471,7 @@ const YES_FLAG = standardFlag("--yes", {
|
|
|
6471
6471
|
schema: boolean()
|
|
6472
6472
|
});
|
|
6473
6473
|
const FORCE_FLAG = standardFlag("--force", {
|
|
6474
|
-
description: "replace
|
|
6474
|
+
description: "replace a file the run writes that already exists; --dry-run lists every file it writes",
|
|
6475
6475
|
takes: "nothing",
|
|
6476
6476
|
schema: boolean()
|
|
6477
6477
|
});
|
|
@@ -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": "330940a",
|
|
6768
6768
|
"dirty": false,
|
|
6769
|
-
"builtAt": "2026-10-
|
|
6769
|
+
"builtAt": "2026-10-28T15:25:01.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
|
}
|
|
@@ -8028,7 +8022,7 @@ async function checkOutputs({ rerun, commandLine, force, files }, outputs) {
|
|
|
8028
8022
|
throw new CliError({
|
|
8029
8023
|
code: OUTPUT_EXISTS,
|
|
8030
8024
|
message: `\`${commandLine}\` would overwrite ${existing.join(", ")}`,
|
|
8031
|
-
hint: `to replace ${them}, run the Next step`,
|
|
8025
|
+
hint: `once the user agrees to replace ${them}, run the Next step`,
|
|
8032
8026
|
next: [rerun({ add: [FORCE_FLAG.flag] })]
|
|
8033
8027
|
});
|
|
8034
8028
|
}
|
|
@@ -8050,7 +8044,7 @@ function outputsGuard(files, cwd) {
|
|
|
8050
8044
|
throw new CliError({
|
|
8051
8045
|
code: ALREADY_EXISTS,
|
|
8052
8046
|
message: error.message,
|
|
8053
|
-
hint: "to replace it, run the Next step",
|
|
8047
|
+
hint: "once the user agrees to replace it, run the Next step",
|
|
8054
8048
|
next: [forceStep]
|
|
8055
8049
|
}, { cause: error });
|
|
8056
8050
|
}
|
|
@@ -8176,7 +8170,7 @@ function editedRemovals(plan, ctx) {
|
|
|
8176
8170
|
return new CliError({
|
|
8177
8171
|
code: OUTPUT_EXISTS,
|
|
8178
8172
|
message: `skill install would remove ${plan.remove.map((file) => join(plan.copy, file)).join(", ")}`,
|
|
8179
|
-
hint: `to remove ${them}, run the Next step`,
|
|
8173
|
+
hint: `once the user agrees to remove ${them}, run the Next step`,
|
|
8180
8174
|
next: [ctx.rerun({ add: [FORCE_FLAG.flag] })]
|
|
8181
8175
|
});
|
|
8182
8176
|
}
|
|
@@ -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`);
|
|
@@ -10710,7 +10706,7 @@ const CONFIRMATION_MISMATCH = "CONFIRMATION_MISMATCH";
|
|
|
10710
10706
|
function checkConfirmation({ rerun, commandLine, given }, expected) {
|
|
10711
10707
|
if (given === expected) return;
|
|
10712
10708
|
const advice = {
|
|
10713
|
-
hint: "
|
|
10709
|
+
hint: "once the user confirms the target, run the Next step",
|
|
10714
10710
|
next: [rerun({
|
|
10715
10711
|
remove: [CONFIRM_FLAG.flag],
|
|
10716
10712
|
add: [CONFIRM_FLAG.flag, expected]
|
|
@@ -10736,7 +10732,7 @@ function checkCost({ rerun, commandLine, yes }, cost) {
|
|
|
10736
10732
|
throw new CliError({
|
|
10737
10733
|
code: NOT_CONFIRMED,
|
|
10738
10734
|
message: `\`${commandLine}\` needs ${YES_FLAG.flag}: ${cost}`,
|
|
10739
|
-
hint: "
|
|
10735
|
+
hint: "once the user approves the cost, run the Next step",
|
|
10740
10736
|
next: [rerun({ add: [YES_FLAG.flag] })]
|
|
10741
10737
|
});
|
|
10742
10738
|
}
|
|
@@ -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.20.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.20.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.20.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.
|
|
@@ -47,18 +47,10 @@ Pass `--json` whenever you parse the output: stdout then holds only the data. A
|
|
|
47
47
|
|
|
48
48
|
## Recovering from errors
|
|
49
49
|
|
|
50
|
-
Errors go to stderr and end with a `hint:` line, then a `run:` line for each command to run next, in order. Under `--json`, every stderr line is one JSON object: `{"progress":"..."}` or `{"notice":"..."}` while it runs, then `{ errors: [{ code, message, hint, next }] }` as the last line, `next` holding those commands when there are any.
|
|
50
|
+
Errors go to stderr and end with a `hint:` line, then a `run:` line for each command to run next, in order. Under `--json`, every stderr line is one JSON object: `{"progress":"..."}` or `{"notice":"..."}` while it runs, then `{ errors: [{ code, message, hint, next }] }` as the last line, `next` holding those commands when there are any. Do what the hint says, except for a code below:
|
|
51
51
|
|
|
52
|
-
| Code
|
|
52
|
+
| Code | What to do |
|
|
53
53
|
|---|---|
|
|
54
|
-
| `
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
| exit 1 | You called it wrong. Fix what the message and hint point at, then run it again. |
|
|
58
|
-
| exit 2 | Something on this machine needs a person. Tell the user what the hint says and ask them to fix it. |
|
|
59
|
-
| exit 3 | The source it talks to is broken. Tell the user; running it again won't help. |
|
|
60
|
-
| exit 75 | Nobody needs to act. Wait, then run it again, as the hint says. |
|
|
61
|
-
| exit 70, `INTERNAL` | A bug in {{binName}}. Run it once more with `{{debugVariable}}=1` for the stack trace, then give the user stderr verbatim. |
|
|
62
|
-
| exit 130 or 143 | It was interrupted. Run it again only if the user still wants it. |
|
|
63
|
-
|
|
64
|
-
<!-- Add a row above the exit rows for each CliError code {{binName}} throws, saying what to do about it. -->
|
|
54
|
+
| `LANGUAGE_UNSUPPORTED` | The user asked for that language. Ask whether an English card will do before running the Next step. |
|
|
55
|
+
|
|
56
|
+
<!-- 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. -->
|
|
@@ -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
|
|