@duyquangnvx/create-agent-cli 0.14.0 → 0.15.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/dist/bin.js +126 -52
- package/package.json +2 -2
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.15.0 (2026-10-27)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- A Packaged-skill CLI gives the `skill` row's problem as a Notice before every Command run from an install, Built-in Commands aside: a missing copy or one of another `metadata.version` (with ``run `<run-as> skill install` ``), or a personal copy under `~/.claude/skills`. A file it cannot read gives a Notice that the copy could not be checked, and the Command still runs.
|
|
12
|
+
- `doctor` of a Packaged-skill CLI has an advisory `agents` row: a project root `AGENTS.md` without the CLI's block, or with one `skill install` would rewrite, is a problem whose fix is `<run-as> skill install`; a `CLAUDE.md` without an `@AGENTS.md` line is one with no fix. The agent-cli skill says a CLI need not check either itself.
|
|
13
|
+
- An Error table entry can name a string key of its `details` in `print`, such as `print: "stack"`, which text mode prints between the message and the hint, so a CLI need not repeat it in the message. `--json` and help JSON are unchanged. The agent-cli skill covers it.
|
|
14
|
+
|
|
15
|
+
### Upgrade from v0.14.0
|
|
16
|
+
|
|
17
|
+
- A Packaged-skill CLI that gives its own Notice of an outdated Skill copy, or has its own `doctor` row for its `AGENTS.md` block, deletes it: the Runtime now gives both.
|
|
18
|
+
|
|
7
19
|
## v0.14.0 (2026-10-27)
|
|
8
20
|
|
|
9
21
|
### Added
|
package/dist/bin.js
CHANGED
|
@@ -5869,13 +5869,22 @@ function debugTrace(error, format) {
|
|
|
5869
5869
|
function errorsOf(error) {
|
|
5870
5870
|
return error instanceof AggregateError && error.errors.length > 0 ? error.errors.flatMap(errorsOf) : [error];
|
|
5871
5871
|
}
|
|
5872
|
+
/** The `details` field that `error`'s code declares for text mode to print, when it holds a string. */
|
|
5873
|
+
function printedDetail(error, format) {
|
|
5874
|
+
if (!(error instanceof CliError)) return void 0;
|
|
5875
|
+
const key = format.errors[error.code]?.print;
|
|
5876
|
+
const { details } = error;
|
|
5877
|
+
if (key === void 0 || typeof details !== "object" || details === null) return void 0;
|
|
5878
|
+
const value = Reflect.get(details, key);
|
|
5879
|
+
return typeof value === "string" ? value : void 0;
|
|
5880
|
+
}
|
|
5872
5881
|
function errorLines(error, format) {
|
|
5873
5882
|
const fields = fieldsOf$1(error, format);
|
|
5874
|
-
const
|
|
5883
|
+
const blocks = [printedDetail(error, format), debugTrace(error, format)].filter((block) => block !== void 0);
|
|
5875
5884
|
const { style } = format;
|
|
5876
5885
|
const label = style.red(format.isBug(error) ? "internal error:" : "error:");
|
|
5877
|
-
const
|
|
5878
|
-
return `${label} ${fields.message}\n${
|
|
5886
|
+
const between = blocks.map((block) => `${block}\n`).join("");
|
|
5887
|
+
return `${label} ${fields.message}\n${between}${style.cyan("hint:")} ${fields.hint}\n`;
|
|
5879
5888
|
}
|
|
5880
5889
|
function formatRuntimeError(error, format) {
|
|
5881
5890
|
const errors = errorsOf(error);
|
|
@@ -7435,6 +7444,87 @@ function dependencyRelease(cli, install, { cwd, root, standing }) {
|
|
|
7435
7444
|
};
|
|
7436
7445
|
}
|
|
7437
7446
|
//#endregion
|
|
7447
|
+
//#region ../agent-cli/dist/built-ins/agents-md.js
|
|
7448
|
+
/** One paragraph telling every session that the CLI exists, how to run it and which skill to load. */
|
|
7449
|
+
function agentsBlock(cli) {
|
|
7450
|
+
const { install } = cli;
|
|
7451
|
+
const where = install?.mode === "dependency" ? `, and only from a package that has \`${install.package}\` in its dependencies; from anywhere else npx reports the package missing` : "";
|
|
7452
|
+
const what = cli.description.replace(/\.$/, "");
|
|
7453
|
+
return `\`${cli.name}\`: ${what}. Run it as \`${runAsOf(cli)}\`${where}. Load the \`${cli.name}\` skill before using it.`;
|
|
7454
|
+
}
|
|
7455
|
+
function markersOf(name) {
|
|
7456
|
+
return {
|
|
7457
|
+
begin: `<!-- BEGIN:${name} -->`,
|
|
7458
|
+
end: `<!-- END:${name} -->`
|
|
7459
|
+
};
|
|
7460
|
+
}
|
|
7461
|
+
/** `text` with the CLI's block between its markers, appended when they are missing; nothing else changes. */
|
|
7462
|
+
function withBlock(cli, text) {
|
|
7463
|
+
const { begin, end } = markersOf(cli.name);
|
|
7464
|
+
const whole = `${begin}\n${agentsBlock(cli)}\n${end}`;
|
|
7465
|
+
const start = text.indexOf(begin);
|
|
7466
|
+
const stop = start === -1 ? -1 : text.indexOf(end, start);
|
|
7467
|
+
if (stop !== -1) return `${text.slice(0, start)}${whole}${text.slice(stop + end.length)}`;
|
|
7468
|
+
if (text === "") return `${whole}\n`;
|
|
7469
|
+
return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
|
|
7470
|
+
}
|
|
7471
|
+
async function agentsMdAt(cli, root) {
|
|
7472
|
+
const path = join(root, "AGENTS.md");
|
|
7473
|
+
const existing = await orMissing(readFile(path, "utf8"));
|
|
7474
|
+
const block = agentsBlock(cli);
|
|
7475
|
+
if (existing === void 0) return {
|
|
7476
|
+
path,
|
|
7477
|
+
block,
|
|
7478
|
+
change: "create"
|
|
7479
|
+
};
|
|
7480
|
+
if (withBlock(cli, existing) === existing) return {
|
|
7481
|
+
path,
|
|
7482
|
+
block,
|
|
7483
|
+
change: null
|
|
7484
|
+
};
|
|
7485
|
+
return {
|
|
7486
|
+
path,
|
|
7487
|
+
block,
|
|
7488
|
+
change: existing.includes(markersOf(cli.name).begin) ? "rewrite" : "add"
|
|
7489
|
+
};
|
|
7490
|
+
}
|
|
7491
|
+
async function handEditsAt(root) {
|
|
7492
|
+
const claudeMd = join(root, "CLAUDE.md");
|
|
7493
|
+
const text = await orMissing(readFile(claudeMd, "utf8"));
|
|
7494
|
+
if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
|
|
7495
|
+
return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
|
|
7496
|
+
}
|
|
7497
|
+
function agentsRow(cli) {
|
|
7498
|
+
return {
|
|
7499
|
+
name: "agents",
|
|
7500
|
+
advisory: true,
|
|
7501
|
+
run: async (ctx) => {
|
|
7502
|
+
const root = await projectRootOf(ctx.cwd);
|
|
7503
|
+
const { path, change } = await agentsMdAt(cli, root);
|
|
7504
|
+
const fix = `${runAsOf(cli)} skill install`;
|
|
7505
|
+
if (change === "rewrite") return {
|
|
7506
|
+
state: "problem",
|
|
7507
|
+
message: `${path} holds a ${cli.name} block other than the one ${fix} writes`,
|
|
7508
|
+
fix
|
|
7509
|
+
};
|
|
7510
|
+
if (change !== null) return {
|
|
7511
|
+
state: "problem",
|
|
7512
|
+
message: `${path} holds no ${cli.name} block, so sessions in this repo do not know ${cli.name}`,
|
|
7513
|
+
fix
|
|
7514
|
+
};
|
|
7515
|
+
const holds = `${path} holds the ${cli.name} block`;
|
|
7516
|
+
const [edit] = await handEditsAt(root);
|
|
7517
|
+
return edit === void 0 ? {
|
|
7518
|
+
state: "ok",
|
|
7519
|
+
message: holds
|
|
7520
|
+
} : {
|
|
7521
|
+
state: "problem",
|
|
7522
|
+
message: `${holds}; ${edit}`
|
|
7523
|
+
};
|
|
7524
|
+
}
|
|
7525
|
+
};
|
|
7526
|
+
}
|
|
7527
|
+
//#endregion
|
|
7438
7528
|
//#region ../agent-cli/dist/built-ins/skills.js
|
|
7439
7529
|
const SKILL_DIRS = [[".claude", "skills"], [".agents", "skills"]];
|
|
7440
7530
|
const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
@@ -7584,6 +7674,22 @@ async function packagedSkillFinding(cli, folder, ctx) {
|
|
|
7584
7674
|
skill
|
|
7585
7675
|
};
|
|
7586
7676
|
}
|
|
7677
|
+
/** Gives a Packaged skill's `skill` row problem as a Notice, since an agent rarely runs `doctor`. */
|
|
7678
|
+
async function noticeSkillCopy(cli, { builtIn, bin }, ctx) {
|
|
7679
|
+
if (builtIn || bin === void 0 || cli.commands === void 0) return;
|
|
7680
|
+
const folder = packagedFolderOf(cli);
|
|
7681
|
+
if (folder === void 0) return;
|
|
7682
|
+
let finding;
|
|
7683
|
+
try {
|
|
7684
|
+
finding = await packagedSkillFinding(cli, folder, ctx);
|
|
7685
|
+
} catch (error) {
|
|
7686
|
+
if (errnoOf(error) === void 0) throw error;
|
|
7687
|
+
ctx.notice(`could not check the ${cli.name} skill: ${messageOf(error)}`);
|
|
7688
|
+
return;
|
|
7689
|
+
}
|
|
7690
|
+
if (finding.state !== "problem") return;
|
|
7691
|
+
ctx.notice(finding.fix === void 0 ? finding.message : `${finding.message}; run \`${finding.fix}\``);
|
|
7692
|
+
}
|
|
7587
7693
|
//#endregion
|
|
7588
7694
|
//#region ../agent-cli/dist/built-ins/doctor-rows.js
|
|
7589
7695
|
function ok(message) {
|
|
@@ -7778,7 +7884,8 @@ function doctorChecks(cli, launch, read) {
|
|
|
7778
7884
|
...toolRows(cli.tools ?? [], cli.commands),
|
|
7779
7885
|
installRow(cli, launch, survey),
|
|
7780
7886
|
registryRow(cli, launch, survey),
|
|
7781
|
-
skillRow(cli)
|
|
7887
|
+
skillRow(cli),
|
|
7888
|
+
...packagedFolderOf(cli) === void 0 ? [] : [agentsRow(cli)]
|
|
7782
7889
|
];
|
|
7783
7890
|
}
|
|
7784
7891
|
//#endregion
|
|
@@ -8245,46 +8352,11 @@ async function sameContent(left, right) {
|
|
|
8245
8352
|
const [a, b] = await Promise.all([readFile(left), readFile(right)]);
|
|
8246
8353
|
return a.equals(b);
|
|
8247
8354
|
}
|
|
8248
|
-
/** One paragraph telling every session that the CLI exists, how to run it and which skill to load. */
|
|
8249
|
-
function agentsBlock(cli) {
|
|
8250
|
-
const { install } = cli;
|
|
8251
|
-
const where = install?.mode === "dependency" ? `, and only from a package that has \`${install.package}\` in its dependencies; from anywhere else npx reports the package missing` : "";
|
|
8252
|
-
const what = cli.description.replace(/\.$/, "");
|
|
8253
|
-
return `\`${cli.name}\`: ${what}. Run it as \`${runAsOf(cli)}\`${where}. Load the \`${cli.name}\` skill before using it.`;
|
|
8254
|
-
}
|
|
8255
|
-
function markersOf(name) {
|
|
8256
|
-
return {
|
|
8257
|
-
begin: `<!-- BEGIN:${name} -->`,
|
|
8258
|
-
end: `<!-- END:${name} -->`
|
|
8259
|
-
};
|
|
8260
|
-
}
|
|
8261
|
-
/** `text` with the block between its markers, appended when they are missing; nothing else changes. */
|
|
8262
|
-
function withBlock(text, block, { begin, end }) {
|
|
8263
|
-
const whole = `${begin}\n${block}\n${end}`;
|
|
8264
|
-
const start = text.indexOf(begin);
|
|
8265
|
-
const stop = start === -1 ? -1 : text.indexOf(end, start);
|
|
8266
|
-
if (stop !== -1) return `${text.slice(0, start)}${whole}${text.slice(stop + end.length)}`;
|
|
8267
|
-
if (text === "") return `${whole}\n`;
|
|
8268
|
-
return `${text}${text.endsWith("\n") ? "" : "\n"}\n${whole}\n`;
|
|
8269
|
-
}
|
|
8270
|
-
function blockChangeOf(existing, block, markers) {
|
|
8271
|
-
if (existing === void 0) return "create";
|
|
8272
|
-
if (withBlock(existing, block, markers) === existing) return null;
|
|
8273
|
-
return existing.includes(markers.begin) ? "rewrite" : "add";
|
|
8274
|
-
}
|
|
8275
|
-
async function handEditsAt(root) {
|
|
8276
|
-
const claudeMd = join(root, "CLAUDE.md");
|
|
8277
|
-
const text = await orMissing(readFile(claudeMd, "utf8"));
|
|
8278
|
-
if (text === void 0 || text.split(/\r?\n/).some((line) => line.trim() === "@AGENTS.md")) return [];
|
|
8279
|
-
return [`add a line \`@AGENTS.md\` to ${claudeMd}, so Claude Code reads AGENTS.md`];
|
|
8280
|
-
}
|
|
8281
8355
|
async function planOf(cli, folder, cwd, packageRoot) {
|
|
8282
8356
|
const source = join(packageRoot, folder);
|
|
8283
8357
|
const { root, copy } = await skillCopyOf(cli, cwd);
|
|
8284
8358
|
const [shipped, present] = await Promise.all([filesIn(source), filesIn(copy)]);
|
|
8285
8359
|
const differs = await Promise.all(shipped.map(async (file) => present.includes(file) && !await sameContent(join(source, file), join(copy, file))));
|
|
8286
|
-
const agentsPath = join(root, "AGENTS.md");
|
|
8287
|
-
const block = agentsBlock(cli);
|
|
8288
8360
|
return {
|
|
8289
8361
|
source,
|
|
8290
8362
|
copy,
|
|
@@ -8292,11 +8364,7 @@ async function planOf(cli, folder, cwd, packageRoot) {
|
|
|
8292
8364
|
replace: shipped.filter((_file, index) => differs[index]),
|
|
8293
8365
|
remove: present.filter((file) => !shipped.includes(file)),
|
|
8294
8366
|
sameVersion: (await skillVersionsOf(source, copy)).same,
|
|
8295
|
-
agentsMd:
|
|
8296
|
-
path: agentsPath,
|
|
8297
|
-
block,
|
|
8298
|
-
change: blockChangeOf(await orMissing(readFile(agentsPath, "utf8")), block, markersOf(cli.name))
|
|
8299
|
-
},
|
|
8367
|
+
agentsMd: await agentsMdAt(cli, root),
|
|
8300
8368
|
handEdits: await handEditsAt(root)
|
|
8301
8369
|
};
|
|
8302
8370
|
}
|
|
@@ -8357,10 +8425,10 @@ function skillInstallCommand(cli, folder) {
|
|
|
8357
8425
|
if (plan.sameVersion && plan.remove.length > 0 && !ctx.force) throw editedRemovals(plan, ctx);
|
|
8358
8426
|
for (const file of [...plan.write, ...plan.replace]) await ctx.files.write(join(plan.copy, file), await readFile(join(plan.source, file)));
|
|
8359
8427
|
for (const file of plan.remove) await ctx.files.remove(join(plan.copy, file));
|
|
8360
|
-
const { path,
|
|
8428
|
+
const { path, change } = plan.agentsMd;
|
|
8361
8429
|
if (change !== null) {
|
|
8362
8430
|
const existing = await orMissing(readFile(path, "utf8")) ?? "";
|
|
8363
|
-
await ctx.files.write(path, withBlock(
|
|
8431
|
+
await ctx.files.write(path, withBlock(cli, existing));
|
|
8364
8432
|
}
|
|
8365
8433
|
return {
|
|
8366
8434
|
copy: plan.copy,
|
|
@@ -8860,7 +8928,10 @@ function agentCli() {
|
|
|
8860
8928
|
}
|
|
8861
8929
|
//#endregion
|
|
8862
8930
|
//#region ../agent-cli/dist/error-table.js
|
|
8863
|
-
/**
|
|
8931
|
+
/**
|
|
8932
|
+
* Keeps each code's literal class and `details` schema, so `cliError` can type its throw sites;
|
|
8933
|
+
* `print` must name a string key of the code's `details`.
|
|
8934
|
+
*/
|
|
8864
8935
|
function defineErrors(table) {
|
|
8865
8936
|
return table;
|
|
8866
8937
|
}
|
|
@@ -9952,7 +10023,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
9952
10023
|
let served = false;
|
|
9953
10024
|
let settled;
|
|
9954
10025
|
try {
|
|
9955
|
-
const outcome = await control.settle(
|
|
10026
|
+
const outcome = await control.settle(noticeSkillCopy(cli, {
|
|
10027
|
+
builtIn,
|
|
10028
|
+
bin: options.bin
|
|
10029
|
+
}, ctx).then(() => execute(command, input, ctx, {
|
|
9956
10030
|
dryRun: flags.dryRun,
|
|
9957
10031
|
confirm: flags.confirm,
|
|
9958
10032
|
yes: flags.yes,
|
|
@@ -9965,7 +10039,7 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
9965
10039
|
},
|
|
9966
10040
|
files,
|
|
9967
10041
|
guardOutputs: outputs.guard
|
|
9968
|
-
}));
|
|
10042
|
+
})));
|
|
9969
10043
|
if (served) return {
|
|
9970
10044
|
result: STOPPED_RESULT,
|
|
9971
10045
|
finishSideTask
|
|
@@ -10148,14 +10222,14 @@ async function runProcess(cli) {
|
|
|
10148
10222
|
}
|
|
10149
10223
|
//#endregion
|
|
10150
10224
|
//#region package.json
|
|
10151
|
-
var version$1 = "0.
|
|
10225
|
+
var version$1 = "0.15.0";
|
|
10152
10226
|
var description = "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.";
|
|
10153
10227
|
//#endregion
|
|
10154
10228
|
//#region src/build.ts
|
|
10155
10229
|
const build = {
|
|
10156
|
-
"sha": "
|
|
10230
|
+
"sha": "cd3df50",
|
|
10157
10231
|
"dirty": false,
|
|
10158
|
-
"builtAt": "2026-10-
|
|
10232
|
+
"builtAt": "2026-10-27T04:23:15.000Z"
|
|
10159
10233
|
};
|
|
10160
10234
|
//#endregion
|
|
10161
10235
|
//#region src/fill.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@duyquangnvx/agent-cli": "0.
|
|
23
|
+
"@duyquangnvx/agent-cli": "0.15.0",
|
|
24
24
|
"tsdown": "0.23.0",
|
|
25
25
|
"zod": "4.6.7"
|
|
26
26
|
},
|