@duyquangnvx/create-agent-cli 0.21.0 → 0.22.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
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.22.0 (2026-11-02)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- The hint of `UNKNOWN_OPTION`, `UNKNOWN_COMMAND` and `INVALID_INPUT` names each Skill copy whose `metadata.version` differs from the build's, for a Skill Hub or a Packaged skill, and says to run `doctor` to update it. A CLI's skill can drop its line on a flag or Command it names that the CLI does not know.
|
|
12
|
+
- The scaffolded setup reference of a Global install says what to do when the user installed the CLI but the shell finds none, under nvm. A CLI's skill can drop its own `command not found` row.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `REGISTRY_AUTH` from npm's `E403` under a Coding agent hints that the agent's sandbox may refuse the registry: the user types `!` and runs it again, or allows the registry in the sandbox's network list, and logs in only if it fails outside the sandbox too. `update`, the install and `doctor`'s `registry` row all give it. A CLI's skill can drop its row saying that `REGISTRY_AUTH` is no refused login, and its `INTERNAL` row too: that hint already names `<NAME>_DEBUG=1`.
|
|
17
|
+
- The scaffolded skill drops what the agent sees anyway: `SKILL.md` loses its When to use section, which belongs in the description, its paragraph on `--json`, `--dry-run` and `--confirm`, and its description of the error format; the setup reference's Check no longer describes `doctor`'s rows. The shipping guide says a skill line earns its place only when no `--help`, output, hint or `doctor` row says it.
|
|
18
|
+
|
|
7
19
|
## v0.21.0 (2026-10-29)
|
|
8
20
|
|
|
9
21
|
### Added
|
package/dist/bin.js
CHANGED
|
@@ -7081,9 +7081,9 @@ function secretRows(secrets, commands) {
|
|
|
7081
7081
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7082
7082
|
function buildStamp() {
|
|
7083
7083
|
return {
|
|
7084
|
-
"sha": "
|
|
7084
|
+
"sha": "4664298",
|
|
7085
7085
|
"dirty": false,
|
|
7086
|
-
"builtAt": "2026-
|
|
7086
|
+
"builtAt": "2026-11-02T04:03:08.000Z"
|
|
7087
7087
|
};
|
|
7088
7088
|
}
|
|
7089
7089
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -7092,6 +7092,40 @@ function versionLine({ version }) {
|
|
|
7092
7092
|
return build === void 0 ? version : `${version} (${build.sha}${build.dirty ? "+dirty" : ""}) built ${build.builtAt}`;
|
|
7093
7093
|
}
|
|
7094
7094
|
//#endregion
|
|
7095
|
+
//#region ../agent-cli/dist/context/agent.js
|
|
7096
|
+
const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
|
|
7097
|
+
const isSet = (name) => (env) => (env[name] ?? "") !== "";
|
|
7098
|
+
const equals = (name, value) => (env) => env[name] === value;
|
|
7099
|
+
const MARKERS = [
|
|
7100
|
+
(env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
|
|
7101
|
+
equals("AGENT", "amp"),
|
|
7102
|
+
isSet("CODEX_SANDBOX"),
|
|
7103
|
+
isSet("CODEX_CI"),
|
|
7104
|
+
isSet("CODEX_THREAD_ID"),
|
|
7105
|
+
isSet("GEMINI_CLI"),
|
|
7106
|
+
isSet("COPILOT_CLI"),
|
|
7107
|
+
isSet("OPENCODE"),
|
|
7108
|
+
isSet("ANTIGRAVITY_AGENT"),
|
|
7109
|
+
isSet("AUGMENT_AGENT"),
|
|
7110
|
+
isSet("REPL_ID"),
|
|
7111
|
+
isSet("CLAUDE_CODE_IS_COWORK"),
|
|
7112
|
+
isSet("CLAUDECODE"),
|
|
7113
|
+
isSet("CLAUDE_CODE"),
|
|
7114
|
+
isSet("CURSOR_TRACE_ID"),
|
|
7115
|
+
isSet("CURSOR_AGENT"),
|
|
7116
|
+
equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
|
|
7117
|
+
equals("TERM_PROGRAM", "kiro"),
|
|
7118
|
+
(env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
|
|
7119
|
+
isSet("GOOSE_PROVIDER")
|
|
7120
|
+
];
|
|
7121
|
+
/**
|
|
7122
|
+
* Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
|
|
7123
|
+
* in a PTY, so a TTY alone doesn't mean a person is watching.
|
|
7124
|
+
*/
|
|
7125
|
+
function isAgent(env) {
|
|
7126
|
+
return MARKERS.some((marker) => marker(env));
|
|
7127
|
+
}
|
|
7128
|
+
//#endregion
|
|
7095
7129
|
//#region ../agent-cli/dist/built-ins/semver.js
|
|
7096
7130
|
const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
7097
7131
|
const ENGINE = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
|
|
@@ -7231,8 +7265,14 @@ function tail$1(stderr) {
|
|
|
7231
7265
|
const lines = lastLines(stderr, STDERR_TAIL_LINES);
|
|
7232
7266
|
return lines === "" ? "" : `\n${lines}`;
|
|
7233
7267
|
}
|
|
7268
|
+
function authHint(code, ctx) {
|
|
7269
|
+
const { label: registry, loginHint } = ctx.registry;
|
|
7270
|
+
const login = `${loginHint}, then ${RUN_AGAIN}`;
|
|
7271
|
+
if (code !== "E403" || !isAgent(ctx.env)) return login;
|
|
7272
|
+
return `a Coding agent's sandbox may refuse ${registry} with a 403: ask your user to type ! and ${RUN_AGAIN}, or to allow ${registry} in the sandbox's network list; if it fails outside the sandbox too, ${login}`;
|
|
7273
|
+
}
|
|
7234
7274
|
function failureOf(call, answer, ctx) {
|
|
7235
|
-
const { label: registry, package: name,
|
|
7275
|
+
const { label: registry, package: name, unreachableHint } = ctx.registry;
|
|
7236
7276
|
const code = NPM_CODE.exec(answer.stderr)?.[1];
|
|
7237
7277
|
const output = tail$1(answer.stderr);
|
|
7238
7278
|
const next = [ctx.rerun];
|
|
@@ -7245,7 +7285,7 @@ function failureOf(call, answer, ctx) {
|
|
|
7245
7285
|
if (code !== void 0 && AUTH_CODES.has(code)) return new CliError({
|
|
7246
7286
|
code: REGISTRY_AUTH,
|
|
7247
7287
|
message: `${registry} refused npm's credentials (${code})${output}`,
|
|
7248
|
-
hint:
|
|
7288
|
+
hint: authHint(code, ctx),
|
|
7249
7289
|
next
|
|
7250
7290
|
});
|
|
7251
7291
|
if (call === "view" && code === "E404") return noLatest(ctx, `npm found no ${name}@latest on ${registry}: no version of it is published there`);
|
|
@@ -7669,6 +7709,7 @@ function hubSkill(cli, from) {
|
|
|
7669
7709
|
run: async (ctx) => findingOf(await skillCopies(cli.name, cli.version, ctx))
|
|
7670
7710
|
}],
|
|
7671
7711
|
notice: async () => {},
|
|
7712
|
+
differing: async (place) => (await skillCopies(cli.name, cli.version, place)).filter(({ state }) => state !== "current" && state !== "unreadable").map(describeCopy),
|
|
7672
7713
|
nextAt: async (version, ctx) => {
|
|
7673
7714
|
const copies = await skillCopies(cli.name, version, ctx);
|
|
7674
7715
|
const fix = fixFor(cli.name, copies, from);
|
|
@@ -7751,40 +7792,6 @@ function agentsRow(agents, install) {
|
|
|
7751
7792
|
};
|
|
7752
7793
|
}
|
|
7753
7794
|
//#endregion
|
|
7754
|
-
//#region ../agent-cli/dist/context/agent.js
|
|
7755
|
-
const AGENT_NAME = /^[a-zA-Z0-9_-]+$/u;
|
|
7756
|
-
const isSet = (name) => (env) => (env[name] ?? "") !== "";
|
|
7757
|
-
const equals = (name, value) => (env) => env[name] === value;
|
|
7758
|
-
const MARKERS = [
|
|
7759
|
-
(env) => AGENT_NAME.test(env.AI_AGENT ?? ""),
|
|
7760
|
-
equals("AGENT", "amp"),
|
|
7761
|
-
isSet("CODEX_SANDBOX"),
|
|
7762
|
-
isSet("CODEX_CI"),
|
|
7763
|
-
isSet("CODEX_THREAD_ID"),
|
|
7764
|
-
isSet("GEMINI_CLI"),
|
|
7765
|
-
isSet("COPILOT_CLI"),
|
|
7766
|
-
isSet("OPENCODE"),
|
|
7767
|
-
isSet("ANTIGRAVITY_AGENT"),
|
|
7768
|
-
isSet("AUGMENT_AGENT"),
|
|
7769
|
-
isSet("REPL_ID"),
|
|
7770
|
-
isSet("CLAUDE_CODE_IS_COWORK"),
|
|
7771
|
-
isSet("CLAUDECODE"),
|
|
7772
|
-
isSet("CLAUDE_CODE"),
|
|
7773
|
-
isSet("CURSOR_TRACE_ID"),
|
|
7774
|
-
isSet("CURSOR_AGENT"),
|
|
7775
|
-
equals("CURSOR_EXTENSION_HOST_ROLE", "agent-exec"),
|
|
7776
|
-
equals("TERM_PROGRAM", "kiro"),
|
|
7777
|
-
(env) => ["/.pi/agent", "\\.pi\\agent"].some((dir) => (env.PATH ?? "").includes(dir)),
|
|
7778
|
-
isSet("GOOSE_PROVIDER")
|
|
7779
|
-
];
|
|
7780
|
-
/**
|
|
7781
|
-
* Whether a Coding agent, not a person, is running the Agent CLI. Some Coding agents run commands
|
|
7782
|
-
* in a PTY, so a TTY alone doesn't mean a person is watching.
|
|
7783
|
-
*/
|
|
7784
|
-
function isAgent(env) {
|
|
7785
|
-
return MARKERS.some((marker) => marker(env));
|
|
7786
|
-
}
|
|
7787
|
-
//#endregion
|
|
7788
7795
|
//#region ../agent-cli/dist/context/errno.js
|
|
7789
7796
|
const PERMISSION_DENIED = "PERMISSION_DENIED";
|
|
7790
7797
|
const DISK_FULL = "DISK_FULL";
|
|
@@ -8305,6 +8312,15 @@ function packagedSkill(cli, { packaged: folder, agents: text }, install) {
|
|
|
8305
8312
|
run: (ctx) => findingOf(cli, folder, install, ctx)
|
|
8306
8313
|
}, ...agents === void 0 ? [] : [agentsRow(agents, install)]],
|
|
8307
8314
|
notice: (launch, ctx) => notice(cli, folder, install, launch, ctx),
|
|
8315
|
+
differing: async ({ cwd, packageRoot }) => {
|
|
8316
|
+
const { copy } = await skillCopyOf(cli, cwd);
|
|
8317
|
+
const { copyVersion, packageVersion, same } = await skillVersionsOf(join(packageRoot, folder), copy);
|
|
8318
|
+
return copyVersion === void 0 || same ? [] : [compared(cli.name, {
|
|
8319
|
+
copy,
|
|
8320
|
+
copyVersion,
|
|
8321
|
+
packageVersion
|
|
8322
|
+
}, false)];
|
|
8323
|
+
},
|
|
8308
8324
|
nextAt: async (_version, ctx) => {
|
|
8309
8325
|
const { fix } = await findingOf(cli, folder, install, ctx);
|
|
8310
8326
|
return fix === void 0 ? [] : [fix];
|
|
@@ -9356,6 +9372,30 @@ function startSideTask(sideTask, runWith, { clock, failed }) {
|
|
|
9356
9372
|
};
|
|
9357
9373
|
}
|
|
9358
9374
|
//#endregion
|
|
9375
|
+
//#region ../agent-cli/dist/built-ins/skill/usage-hint.js
|
|
9376
|
+
const SKILL_USAGE_CODES = /* @__PURE__ */ new Set([
|
|
9377
|
+
UNKNOWN_OPTION,
|
|
9378
|
+
UNKNOWN_COMMAND,
|
|
9379
|
+
INVALID_INPUT
|
|
9380
|
+
]);
|
|
9381
|
+
/** A usage error whose hint names each Skill copy that differs from this build, since one may have named the input. */
|
|
9382
|
+
async function withDifferingSkill(error, cli, place, runAs) {
|
|
9383
|
+
if (!(error instanceof UsageError) || !SKILL_USAGE_CODES.has(error.code)) return error;
|
|
9384
|
+
let differing;
|
|
9385
|
+
try {
|
|
9386
|
+
differing = await skillOf$1(cli, installOf(cli)).differing(place);
|
|
9387
|
+
} catch (lookup) {
|
|
9388
|
+
if (errnoOf(lookup) === void 0) throw lookup;
|
|
9389
|
+
return error;
|
|
9390
|
+
}
|
|
9391
|
+
if (differing.length === 0) return error;
|
|
9392
|
+
return new UsageError({
|
|
9393
|
+
code: error.code,
|
|
9394
|
+
message: error.message,
|
|
9395
|
+
hint: `${error.hint}; the ${cli.name} skill may name what this build does not know: ${differing.join("; ")}; run \`${runAs} doctor\` to see how to update it`
|
|
9396
|
+
});
|
|
9397
|
+
}
|
|
9398
|
+
//#endregion
|
|
9359
9399
|
//#region ../agent-cli/dist/command-line/suggest.js
|
|
9360
9400
|
const MAX_DISTANCE = 3;
|
|
9361
9401
|
const MIN_SIMILARITY = .4;
|
|
@@ -11066,7 +11106,11 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11066
11106
|
});
|
|
11067
11107
|
});
|
|
11068
11108
|
} catch (error) {
|
|
11069
|
-
return failed(error,
|
|
11109
|
+
return failed(await withDifferingSkill(error, cli, {
|
|
11110
|
+
cwd,
|
|
11111
|
+
env,
|
|
11112
|
+
packageRoot
|
|
11113
|
+
}, invocation.runAs), noSideTask);
|
|
11070
11114
|
}
|
|
11071
11115
|
const { input, flags } = parsed;
|
|
11072
11116
|
resumed = command.deadline?.resume(input, rerunOf);
|
|
@@ -11278,10 +11322,16 @@ async function routeAndRun(cli, argv, options, write, stopNotice) {
|
|
|
11278
11322
|
...format,
|
|
11279
11323
|
json: routed.json
|
|
11280
11324
|
}));
|
|
11281
|
-
case "usage error":
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11325
|
+
case "usage error": {
|
|
11326
|
+
const place = {
|
|
11327
|
+
...options,
|
|
11328
|
+
packageRoot: packageRootOf(options.bin, options.cwd)
|
|
11329
|
+
};
|
|
11330
|
+
return ranAlone(errorResult(await withDifferingSkill(routed.error, cli, place, runAs), {
|
|
11331
|
+
...misused(routed.node),
|
|
11332
|
+
json: routed.json
|
|
11333
|
+
}));
|
|
11334
|
+
}
|
|
11285
11335
|
case "command": return whileStopping(options.signal, stopNotice === void 0 ? void 0 : () => write.stderr(stopLine(stopNotice, routed.json)), () => runCommand(cli, routed, options, write, misused(routed.spec)));
|
|
11286
11336
|
}
|
|
11287
11337
|
}
|
|
@@ -11344,7 +11394,7 @@ async function runProcess(cli) {
|
|
|
11344
11394
|
//#region package.json
|
|
11345
11395
|
var package_default = {
|
|
11346
11396
|
name: "@duyquangnvx/create-agent-cli",
|
|
11347
|
-
version: "0.
|
|
11397
|
+
version: "0.22.0",
|
|
11348
11398
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11349
11399
|
type: "module",
|
|
11350
11400
|
engines: { "node": ">=22.12.0" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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.22.0",
|
|
25
25
|
"tsdown": "0.23.0",
|
|
26
26
|
"zod": "4.6.7"
|
|
27
27
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
This installs the CLI. The user has installed this skill already.
|
|
2
2
|
|
|
3
|
-
1. Run `node --version` and compare it with the range `npm view {{packageName}} engines.node` prints, which `doctor`'s `node` row also checks once the CLI is installed. Under nvm every Node version has its own global packages, so {{binName}} lands under the Node this shell runs. If that Node is too old, or is not the one the user's terminal runs, tell the user and let them choose before going on.
|
|
3
|
+
1. Run `node --version` and compare it with the range `npm view {{packageName}} engines.node` prints, which `doctor`'s `node` row also checks once the CLI is installed. Under nvm every Node version has its own global packages, so {{binName}} lands under the Node this shell runs. If that Node is too old, or is not the one the user's terminal runs, tell the user and let them choose before going on. That is also why a {{binName}} the user installed can be missing from this shell: ask which Node their terminal runs before installing again.
|
|
4
4
|
2. Hand the user the install line to type, as [Sandbox](#sandbox) says:
|
|
5
5
|
|
|
6
6
|
`! npm install --global {{packageName}}@latest`
|
|
@@ -7,7 +7,7 @@ metadata:
|
|
|
7
7
|
version: "0.0.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
<!-- A skeleton to rewrite around {{binName}}'s real Commands. Leave flags out: `--help` is generated from the code and stays in sync with it, this file does not. -->
|
|
10
|
+
<!-- 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. What {{binName}} is for, and the nearby tasks it is not for, go in the description, which the agent always sees. -->
|
|
11
11
|
|
|
12
12
|
# {{binName}}
|
|
13
13
|
|
|
@@ -19,13 +19,6 @@ metadata:
|
|
|
19
19
|
|
|
20
20
|
The line above should show 0.0.0, the version this skill describes. If it says {{binName}} is not installed or shows another version, read [setup](references/setup.md) before anything else.
|
|
21
21
|
|
|
22
|
-
## When to use
|
|
23
|
-
|
|
24
|
-
<!-- The tasks {{binName}} is for, and the nearby tasks it is not for. -->
|
|
25
|
-
|
|
26
|
-
- Greeting someone by name.
|
|
27
|
-
- Writing a greeting card to a file, in English for free or in another language at a cost.
|
|
28
|
-
|
|
29
22
|
## Workflows
|
|
30
23
|
|
|
31
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. -->
|
|
@@ -43,11 +36,9 @@ The line above should show 0.0.0, the version this skill describes. If it says {
|
|
|
43
36
|
|
|
44
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.
|
|
45
38
|
|
|
46
|
-
Pass `--json` whenever you parse the output: stdout then holds only the data. A Command that changes something takes `--dry-run`, which prints its Plan, the files it writes and its cost, and changes nothing: run it first when unsure. A destructive Command also needs `--confirm=<target>`, and its error's Next step gives the exact command.
|
|
47
|
-
|
|
48
39
|
## Recovering from errors
|
|
49
40
|
|
|
50
|
-
|
|
41
|
+
Do what the error's hint says, except for a code below:
|
|
51
42
|
|
|
52
43
|
| Code | What to do |
|
|
53
44
|
|---|---|
|
|
@@ -8,11 +8,9 @@ Work through a section in order and stop at a step that fails. Ask the user to d
|
|
|
8
8
|
|
|
9
9
|
## Check
|
|
10
10
|
|
|
11
|
-
Run `{{runAs}} doctor --json
|
|
11
|
+
Run `{{runAs}} doctor --json`, which changes nothing. Hand the user each line of its `next` to type with `!`, in order, then run `doctor` again.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
- `next` lists every fix in order. Hand the user each one to type with `!`, then run `doctor` again.
|
|
15
|
-
- {{skillCheck}} If the session does not show it, ask the user to type `/reload-skills`, and failing that to restart Claude Code.
|
|
13
|
+
{{skillCheck}} If the session does not show it, ask the user to type `/reload-skills`, and failing that to restart Claude Code.
|
|
16
14
|
|
|
17
15
|
`{{runAs}} --version` names on its second line the `CHANGELOG.md` installed with the CLI. When the user asks what changed, read it from the top, newest first, down to the version in question.
|
|
18
16
|
|