@duyquangnvx/create-agent-cli 0.5.0 → 0.7.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 +45 -0
- package/README.md +2 -2
- package/dist/bin.js +1963 -1557
- package/package.json +2 -2
- package/template/AGENTS.md +2 -2
- package/template/README.md +8 -0
- package/template/skills/{{binName}}/SKILL.md +1 -1
- package/template/skills/{{binName}}/references/setup.md +47 -0
- package/template/src/cli.ts +1 -0
- package/template/test/cli.test.ts +50 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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.7.0",
|
|
23
23
|
"tsdown": "0.23.0",
|
|
24
24
|
"zod": "4.6.7"
|
|
25
25
|
},
|
package/template/AGENTS.md
CHANGED
|
@@ -23,10 +23,10 @@ Greenfield with no deployments or users: change code directly. Breaking changes
|
|
|
23
23
|
- A Command that writes files names them in `outputs(plan)`, and the Runtime refuses to overwrite one without `--force`. A Command that costs money states it in `cost(plan)`, a sentence or `undefined` when cheap, and the Runtime refuses a costly run without `--yes`.
|
|
24
24
|
- A Command reaches a remote service through a port: an interface in `src/ports.ts`, built by the `ports` factory passed to `defineCli` and read as `ctx.ports`. It writes files through `ctx.files` and runs programs through `ctx.spawn`, so a test replaces each of them.
|
|
25
25
|
- Every error code is declared once in a `defineErrors` table passed to `defineCli({ errors })`, with the Exit class that must act on it and an optional `details` schema, and thrown through `cliError(errors, code, { message, hint })`, whose `hint` names the command to run next. An undeclared code, and anything else thrown, becomes an internal error (exit 70), whose stack trace `{{debugVariable}}=1` shows.
|
|
26
|
-
- A Command that lists many items declares its row schema as `rows` and returns `paginate(items, { limit, cursor })
|
|
26
|
+
- A Command that lists many items declares its row schema as `rows` and returns `paginate(items, { limit, cursor })`, or `page(items, next)` with the source's own cursor when the source pages itself; the Runtime adds `--limit`, `--cursor` and `--fields`.
|
|
27
27
|
- Tests drive the CLI through `runCli`, replacing ports, files and the clock in its options, and assert stdout, stderr, the exit code and what the fakes recorded, as `test/cli.test.ts` does.
|
|
28
28
|
- A change a user of `{{binName}}` would notice gets an entry under `## Unreleased` in `CHANGELOG.md`, in the format its opening paragraph states.
|
|
29
29
|
|
|
30
30
|
## Agent skills
|
|
31
31
|
|
|
32
|
-
`skills/{{binName}}/SKILL.md` is the agent skill: when to use `{{binName}}`, its workflows and how to recover from its errors. Update it with any change to those, and leave flag details to `--help`.
|
|
32
|
+
`skills/{{binName}}/SKILL.md` is the agent skill: when to use `{{binName}}`, its workflows and how to recover from its errors. Update it with any change to those, and leave flag details to `--help`. Its `references/setup.md` installs, checks and updates `{{binName}}`; `test/cli.test.ts` fails when its install line, or the README's, differs from what `doctor` gives.
|
package/template/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
{{description}}
|
|
4
4
|
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Install the skill first, so that the coding agent loading it can install and check the CLI. In Claude Code, type each line in the prompt:
|
|
8
|
+
|
|
9
|
+
{{skillInstall}}
|
|
10
|
+
|
|
11
|
+
`! npm install --global {{packageName}}@latest`
|
|
12
|
+
|
|
5
13
|
Built on [`@duyquangnvx/agent-cli`](https://www.npmjs.com/package/@duyquangnvx/agent-cli): declare Commands in `src/cli.ts` and test them through `runCli`, as `test/cli.test.ts` does.
|
|
6
14
|
|
|
7
15
|
```sh
|
|
@@ -17,7 +17,7 @@ metadata:
|
|
|
17
17
|
|
|
18
18
|
!`{{binName}} --version 2>&1 || echo "{{binName}}: not installed"`
|
|
19
19
|
|
|
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,
|
|
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
22
|
## When to use
|
|
23
23
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
|
|
3
|
+
Checking, installing and updating {{binName}}, and letting it work inside Claude Code's sandbox. Using it is in [SKILL.md](../SKILL.md).
|
|
4
|
+
|
|
5
|
+
Work through a section in order and stop at a step that fails. Ask the user to do what only they can do, such as typing an install line, and wait until they say it is done.
|
|
6
|
+
|
|
7
|
+
<!-- Add a section for each step {{binName}} needs before its first run, such as a sign-in or an API key, and a trial run if a cheap Command makes one. -->
|
|
8
|
+
|
|
9
|
+
## Check
|
|
10
|
+
|
|
11
|
+
Run `{{binName}} doctor --json`. It checks the setup without changing anything and prints `{ rows, next }`.
|
|
12
|
+
|
|
13
|
+
- Each row has a `check`, a `state` (`ok`, `problem` or `skipped`) and a `message` saying what it found. A row that has a `fix` names the command that fixes it.
|
|
14
|
+
- `next` lists every fix in order. Hand the user each one to type with `!`, then run `doctor` again.
|
|
15
|
+
- 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 its `npx -y skills add` fix, since `npx skills update` skips a copy it did not install. After a fix that runs the `skills` installer, load this skill again. If the session does not show it, ask the user to type `/reload-skills`, and failing that to restart Claude Code.
|
|
16
|
+
|
|
17
|
+
`{{binName}} --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
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
This installs the CLI. The user has installed this skill already.
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
2. Hand the user the install line to type, as [Sandbox](#sandbox) says:
|
|
25
|
+
|
|
26
|
+
`! npm install --global {{packageName}}@latest`
|
|
27
|
+
|
|
28
|
+
Never run `npx {{binName}}`: that runs whatever npm package holds the name.
|
|
29
|
+
3. If `{{binName}} --version` then shows an older version than this skill describes, npm answered from a stale cache. Hand the user the same line with `--cache "$(mktemp -d)"` appended.
|
|
30
|
+
4. Finish with [Check](#check). {{binName}} is ready when `doctor` exits 0 with nothing in `next`: a row that only advises, such as `install`, leaves the exit code at 0.
|
|
31
|
+
|
|
32
|
+
## Update
|
|
33
|
+
|
|
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 what changed since the version it replaced: under `--json`, `upgrades` lists each release's notes, oldest first. Do each upgrade step those notes give, oldest first.
|
|
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
|
+
4. Confirm the new version with `{{binName}} --version`.
|
|
38
|
+
|
|
39
|
+
## Sandbox
|
|
40
|
+
|
|
41
|
+
Claude Code's sandbox never lets a command write npm's global folder, `~/.claude/skills` or `~/.agents/skills`, so the install line, `{{binName}} update` and `npx skills` fail inside it. Hand the user the line to type with `!` in this session, which runs it outside the sandbox under the Node this shell runs.
|
|
42
|
+
|
|
43
|
+
`doctor`'s `registry` row, `update` and `npm view` ask the registry {{binName}} is published to for its latest release. If the sandbox refuses that host, `doctor` still passes with only that row failing, and `update --dry-run` fails. If the user wants it to work inside the sandbox, give them this block for their Claude Code settings, naming the registry's host, and let them decide:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{ "sandbox": { "network": { "allowedDomains": ["registry.npmjs.org"] } } }
|
|
47
|
+
```
|
package/template/src/cli.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdtemp, readFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
2
5
|
import { type Files, runCli } from "@duyquangnvx/agent-cli";
|
|
3
6
|
import { describe, expect, test } from "vitest";
|
|
7
|
+
import manifest from "../package.json" with { type: "json" };
|
|
4
8
|
import { cli } from "../src/cli.ts";
|
|
5
9
|
import type { Greetings } from "../src/ports.ts";
|
|
6
10
|
|
|
@@ -155,3 +159,48 @@ describe("card", () => {
|
|
|
155
159
|
});
|
|
156
160
|
});
|
|
157
161
|
});
|
|
162
|
+
|
|
163
|
+
describe("install lines", () => {
|
|
164
|
+
const root = fileURLToPath(new URL("..", import.meta.url));
|
|
165
|
+
|
|
166
|
+
/** Each doctor row's fix, run from this checkout with no npm on PATH and no skill installed. */
|
|
167
|
+
async function doctorFixes(): Promise<Record<string, string | undefined>> {
|
|
168
|
+
const home = await mkdtemp(join(tmpdir(), "{{binName}}-home-"));
|
|
169
|
+
const result = await runCli(cli, ["doctor", "--json"], {
|
|
170
|
+
env: { HOME: home, PATH: "" },
|
|
171
|
+
cwd: root,
|
|
172
|
+
});
|
|
173
|
+
const { rows } = JSON.parse(result.stdout) as { rows: { check: string; fix?: string }[] };
|
|
174
|
+
return Object.fromEntries(rows.map(({ check, fix }) => [check, fix]));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function text(path: string): Promise<string> {
|
|
178
|
+
return readFile(join(root, path), "utf8");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
test("the setup reference and the README install the CLI with doctor's install line", async () => {
|
|
182
|
+
const { install } = await doctorFixes();
|
|
183
|
+
|
|
184
|
+
expect(install).toBeDefined();
|
|
185
|
+
expect(await text("skills/{{binName}}/references/setup.md")).toContain(`\`! ${install}\``);
|
|
186
|
+
expect(await text("README.md")).toContain(`\`! ${install}\``);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test.skipIf(!("repository" in manifest))(
|
|
190
|
+
"the README installs the skill with doctor's skill line",
|
|
191
|
+
async () => {
|
|
192
|
+
const { skill } = await doctorFixes();
|
|
193
|
+
|
|
194
|
+
expect(skill).toBeDefined();
|
|
195
|
+
expect(await text("README.md")).toContain(`\`! ${skill}\``);
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
test("the setup reference names the registry's host for the sandbox to allow", async () => {
|
|
200
|
+
const { host } = new URL(manifest.publishConfig.registry);
|
|
201
|
+
|
|
202
|
+
expect(await text("skills/{{binName}}/references/setup.md")).toContain(
|
|
203
|
+
`"allowedDomains": ["${host}"]`,
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
});
|