@duyquangnvx/create-agent-cli 0.3.0 → 0.5.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,74 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## v0.5.0 (2026-10-19)
8
+
9
+ ### Added
10
+
11
+ - A command-tree CLI may declare `defineCli({ tools })`, the tools `doctor` checks besides its Commands' own. `doctor` reports one row per tool from the CLI's `tools` and every Command's, once per name, a missing one as a problem with its `install` as the fix. A Command still requires only its own `tools`, so the CLI's list never makes a Command refuse with `TOOL_MISSING`.
12
+ - A Command may declare `hidden: true`. It routes and runs when named, and its own `--help` works, but it is left out of text help, `--help --json`, the root examples and "did you mean" suggestions for a misspelt Command.
13
+ - A Command may declare `operator: true`, an Operator Command only a person at a terminal runs. When a Coding agent is detected, the run fails before its input is read, stdin included, with the new Runtime code `NEEDS_OPERATOR` (exit 2, exported as a constant), whose hint is `ask the operator to type: ! <command line as typed>`; `--json` alone does not refuse. Text help marks it `(operator only)` in a listing and with an `Operator Command:` line in its own help, and `--help --json` carries `operator` on every Command, `true` on an Operator Command and `false` on the rest.
14
+ - A Command may declare `help`, a paragraph printed as written after the options in text help. `--help --json` carries it as `help`, which is `null` on a Command without one.
15
+ - `ctx.spawn(command, args, { progress: true })` passes each line of the child's stdout and stderr through `ctx.progress` as it arrives, and still resolves to the whole output, which `check: true` uses for `TOOL_FAILED`. A `Spawn` passed as `runCli`'s `spawn` option receives an `onLine` callback when the caller asked for progress, to emit the child's lines.
16
+ - On Windows, `ctx.spawn` runs a `.cmd` or `.bat` tool, such as one npm installed, found on `PATH` through `PATHEXT` or given by its path. Each argument reaches the tool as given: it is quoted, then escaped for `cmd.exe`, which otherwise expands `%VAR%` and acts on `&`, `|`, `<`, `>`, `^`, `(`, `)` and `!` even inside quotes, twice for a `.cmd` shim that hands its arguments to a second `cmd.exe` parse, as cross-spawn does.
17
+ - A field of a Command's `options` may be a `z.array(...)`, optionally wrapped in optional or default, which collects every occurrence of its flag in order: `--ref a --ref=b --ref c` gives `ref: ["a", "b", "c"]`. Each value is coerced like a scalar option of the element's type, and an issue on one names it, as in `--row "x": ...`. None gives an empty array for the schema to judge, or leaves an optional field out so its default applies. Text help shows the flag as `--ref <value>...`, and `--help --json` gives it the array schema. `defineCommand` throws for an array of booleans. A repeated scalar flag still keeps its last value.
18
+ - `ctx.commandLine({ add?, remove? })` returns the command line as the caller typed it, starting with the CLI's name and shell-quoted, with each flag in `remove` dropped along with its value (`--from 3`, `--from=3`, every time it was given) and the words in `add` placed before any `--`, for a hint naming the exact command to run next. It is the helper the Runtime's own hints (`--confirm`, `--yes`, `--force`, a page's `next`) are built with.
19
+ - A run deadline's `resume(input, commandLine)` receives `ctx.commandLine` as its second argument. A `resume` taking only `input` still type-checks.
20
+ - `ctx.poll(check, { intervalMs, signal })` also stops when `signal` aborts, ending with its reason, so `signal: ctx.deadline(ms)` stops polling at a `--wait` deadline rather than one interval after it.
21
+ - `INTERRUPTED` and `DEADLINE_EXCEEDED` are exported as constants, so a Command tells `ctx.signal.reason` apart and throws its own `INTERRUPTED` with a hint it computes, which wins over the Runtime's. The skill teaches it.
22
+ - A `doctor` row may return `{ state: "skipped", message }` for a check it did not run, such as "not checked: the bridge is down". It shows in text and `--json` and never fails `doctor`.
23
+ - On Ctrl-C or SIGTERM, `doctor` prints the rows finished so far, in text or `--json`, before the run ends with 130 or 143. Rows cut short are left out.
24
+ - With no copy of the skill installed, `doctor`'s `skill` row gives the fix `npx -y skills add <source> --skill <cli name> -g`, which installs only this CLI's skill from a repo holding several, and stays ok. The source is `defineCli({ skill: { source } })` as given, else the package.json `repository` URL with `git+` dropped, `.git` added if missing and `#release` appended, for an `https`, `http`, `ssh` or `git` URL or an scp-style `git@host:group/repo`; a GitHub `owner/repo` shorthand is kept as it is. With no `repository`, one that is neither, or a package.json that cannot be read or parsed, the row gives no fix.
25
+ - `orStdin(schema)` marks one string field of a Command's `args` as also reading stdin: when its operand is exactly `-`, the Runtime reads stdin to its end as UTF-8, before `plan`, `run` or `start`, and the field's schema judges the text, so an empty or invalid text fails with `INVALID_INPUT` like typed text, the issue naming the field `from stdin` instead of quoting the text. Ctrl-C and the run deadline stop a read that never ends; a deadline hit there hints to pipe the text in. When stdin is a terminal, a progress line first says what is read and how to end it (Ctrl-D, or Ctrl-Z then Enter on Windows). Text help shows the field as `<name|->` (`[name|-]` when optional), and `--help --json` marks it `"stdin": true`. `defineCommand` throws for two such args, or for the marker on an option. `runCli` takes `stdin`, a string or a readable stream, read by a `-`; absent, a `-` reads an empty text. The type `StdinSource` is exported.
26
+ - The ports factory's `PortsBase` adds `progress`, `fileError`, `version`, `build` and `self`, which `Context` carries too: a bridge or store writes progress lines (JSON Lines under `--json`) and classifies a failure of its own file as a Command does. `version` and `build` are the CLI's version and bundler stamp as `--version` prints them, `build` absent when unbundled. `self` is the command line that reruns the CLI, `[process.execPath, ...process.execArgv, bin]` from `runCli`'s `bin`, or `[name]`, found on `PATH`, without one, for spawning a hidden Command. The type `BuildStamp` is exported.
27
+ - `ctx.fileError(error, { path, access })` turns an errno failure of a Command's own file access, such as a SQLite store or a log it opens itself, into the Runtime's classified error, with the same codes and hints as `ctx.files`; any other error comes back unchanged. The type `FileAccess` is exported.
28
+ - The Runtime code `NOT_A_FILE` (exit 1), raised for EISDIR, such as `ctx.files.read` of a folder, naming the path. It is exported as a constant.
29
+ - `runCli` takes a `platform` option, which replaces `process.platform` in picking the wording of hints, the `PERMISSION_DENIED` hint and how to end stdin, so a test checks the Windows wording on any OS.
30
+ - `ctx.files.write(path, data, { exclusive, mode })`: `exclusive: true` fails with `ALREADY_EXISTS` when anything is at the path, never leaving half a file there, and `mode` sets the file's permission bits whatever mode an older file had. The type `WriteOptions` is exported.
31
+ - `ctx.files.stat(path)` returns `{ size, id }`, where `id` is the same for every link to one file (device and inode), or `undefined` when nothing is at the path or a part of it is a file. The type `FileStat` is exported.
32
+ - The agent-cli skill's example `links` has `import <text|->`, which saves every link found in a text, read from stdin when given `-`, tags each with every repeated `--tag`, and explains its stdin form in a `help` paragraph. The scaffolded template's `card` takes an optional message to write under the greeting, read from stdin when given `-`, signs the card from every repeated `--from`, and states its cost in a `help` paragraph; its tests cover each.
33
+
34
+ ### Changed
35
+
36
+ - **Breaking:** `DoctorFinding` is a union over `state`: `ok` (`message`, `fix?`), `problem` (`message`, `fix?`, `code?`) and `skipped` (`message`). A row that throws a `CliError` keeps its code, and `doctor --json` rows carry `code`.
37
+ - **Breaking:** `doctor` fails with one error per non-advisory problem row, with the row's code or `DOCTOR_PROBLEMS` without one, so it exits by the first of 1, 2, 3 and 75 that any of them has, not always 2: an expired session exits 3, a network failure 75. Each error's message is `<cli> doctor found a problem in <row>: <message>`. The report still prints on stdout.
38
+ - **Breaking:** the `Files` interface gains `stat`, and its `write` receives the write options, so every fake passed as `runCli`'s `files` option must implement `stat`. The scaffolded template's fake and the skill's example implement it.
39
+ - **Breaking:** more errno are classified, naming the path: EROFS and EBUSY fail with `PERMISSION_DENIED` (exit 2, with the sandbox hint under a Coding agent), EDQUOT with `DISK_FULL` (exit 2), and ENOTDIR and ENAMETOOLONG with `NOT_FOUND` (exit 1). Each was exit 70 before, so a write Claude Code's Linux sandbox denies with EROFS no longer reads as a bug.
40
+ - **Breaking:** for a Command with `outputs`, after every guard passes and before `apply`, the Runtime creates each output's folder and a temp file in it, so a denied folder fails with `PERMISSION_DENIED` naming the folder before `apply` runs; `--dry-run` and a refused guard create nothing. Without `--force`, the first write to each output during `apply`, through `ctx.files` or a port's `files`, is exclusive: it fails with `ALREADY_EXISTS`, whose hint names the command line with `--force`, if the file appeared meanwhile. A later write to an output the run already wrote replaces it; with `--force` every write replaces.
41
+ - **Breaking:** agent detection follows gh's marker list: `AI_AGENT` (an agent name), `AGENT=amp`, `CODEX_SANDBOX`, `CODEX_CI`, `CODEX_THREAD_ID`, `GEMINI_CLI`, `COPILOT_CLI`, `OPENCODE`, `ANTIGRAVITY_AGENT`, `AUGMENT_AGENT`, `REPL_ID`, `CLAUDE_CODE_IS_COWORK`, `CLAUDECODE`, `CLAUDE_CODE`, `CURSOR_TRACE_ID`, `CURSOR_AGENT`, `CURSOR_EXTENSION_HOST_ROLE=agent-exec`, `TERM_PROGRAM=kiro`, a `.pi/agent` folder on `PATH`, or `GOOSE_PROVIDER`. A marker set to an empty string no longer counts, and `CI` alone no longer counts as an agent, so a usage error in a CI job prints the error and hint without the full help, and a CI job on a TTY gets colour.
42
+ - **Breaking:** under `--json`, stderr is JSON Lines (ADR 0006): each `ctx.progress(message)` writes `{"progress":"<message>"}` as it happens, the Runtime's own progress lines (`finishing <name>`, the `stopping… (Ctrl-C again to quit now)` notice, the stdin announcement) and a spawned child's lines under `progress: true` are written the same way, and a failure's `{"errors":[…]}` stays the last line, so every stderr line parses as JSON. Without `--json`, progress stays plain text. The agent-cli skill and the scaffolded skill say so.
43
+ - On Windows, the `PERMISSION_DENIED` hint says a program may hold the path open, and to close it, then run the same command again.
44
+ - `ctx.files.write` through a symlink replaces the link's target and keeps the link.
45
+ - On Windows, a replacing `ctx.files.write` retries a transient EPERM or EBUSY, such as an antivirus scan holding the file, for about half a second before failing with `PERMISSION_DENIED`.
46
+ - `doctor` and `update` run the npm beside the Node running the CLI (`npm`, or `npm.cmd` on Windows), with that Node first on its `PATH`, so under nvm they install into that Node; with no npm beside it, they run the npm on `PATH` as before. `runCli` gains the option `node`, the path `runProcess` passes as `process.execPath`; absent, npm comes from `PATH`.
47
+
48
+ ### Fixed
49
+
50
+ - On Windows, an interrupt kills the child `ctx.spawn` started, or the npm `doctor` and `update` run, together with every process it started (`taskkill /T /F`), so Ctrl-C leaves no orphaned processes.
51
+ - `ctx.files.exists` answers false for a path under a file instead of failing.
52
+
53
+ ## v0.4.0 (2026-10-19)
54
+
55
+ ### Added
56
+
57
+ - The last field of a Command's `args` may be a `z.array(...)`, optionally wrapped in optional or default, which takes every remaining operand: `tool preview a b c` gives `dirs: ["a", "b", "c"]`. None gives an empty array for the schema to judge, or leaves an optional field out so its default applies. Each operand is coerced like a scalar arg of the element's type, and an issue on one names it and its position, as in `<values...> "x" (argument 3): ...`. Text help shows the field as `<name...>` (`[name...]` when optional), and `--help --json` gives it the array schema. `defineCommand` throws when an array field is not the last of `args`.
58
+ - The ports factory receives `PortsBase`, the run's base context: `env`, `cwd`, `packageRoot`, `secret`, `spawn`, `files`, `clock` and `signal`, the same classified, cwd-resolved helpers a Command gets, so an adapter reads a secret or the package root as a Command does. Under `runCli` it sees the test's `env`, `cwd`, `clock`, `files` and `spawn`, and the `ports` option still replaces its result. It runs when a run first reads `ctx.ports`, at most once, and what it throws exits by its Exit class like a Command error; a run that never reads them, such as `doctor` or `update`, never calls it, and a `doctor` row that reads them reports the error as its problem. A factory written as `() => Ports` still type-checks; the type `PortsBase` is exported.
59
+ - A write or destructive Command may declare `force: "<what it replaces>"`, which gives it the Standard flag `--force` with that sentence as the flag's description in help and help JSON. A Command gets `--force` from `outputs`, `force` or both; with `outputs`, `--force` still lifts `OUTPUT_EXISTS`, and `outputs` alone keeps the generic description. `ctx.force` is true only when the caller passed `--force` to a Command that has it, and `plan` sees it under `--dry-run` too. `defineCommand` throws when a read or serve Command declares `force`, or when the sentence is empty.
60
+ - `ctx.files.read(path)` returns a file's bytes as a `Uint8Array`; `ctx.files.list(folder)` returns the names in a folder, sorted, never the Runtime's temp files; `ctx.files.remove(path)` removes a file or a folder with everything in it, and succeeds when the path does not exist; `ctx.files.mkdir(path, { exclusive? })` creates a folder and its missing parents, and with `exclusive: true` fails when the folder exists. Each resolves its path against `ctx.cwd` and fails as `write` does: `PERMISSION_DENIED` (exit 2, with the sandbox hint under a Coding agent), `DISK_FULL` (exit 2) or `NOT_FOUND` (exit 1), naming the path. The type `MkdirOptions` is exported.
61
+ - The Runtime code `ALREADY_EXISTS` (exit 1), raised for EEXIST, such as an exclusive `mkdir` of an existing folder, naming the path, with a hint to pass another path or remove that one. `ALREADY_EXISTS`, `PERMISSION_DENIED`, `DISK_FULL` and `NOT_FOUND` are exported as constants, so a Command catches one with `error instanceof CliError && error.code === ALREADY_EXISTS`.
62
+
63
+ ### Changed
64
+
65
+ - **Breaking:** `doctor` exits 0 when only its `install` or `registry` row is a problem (not a global install, a newer version on the registry, or the registry unreachable). Those rows still show `problem` with their fix, in text and `--json`, and the fix stays under `next`.
66
+ - **Breaking:** the `Files` interface gains `read`, `list`, `remove` and `mkdir`, so every fake passed as `runCli`'s `files` option must implement them. The scaffolded template's fake and the skill's example implement them.
67
+ - **Breaking:** `ctx.files.write` creates missing parent folders, so a write into a missing folder no longer fails with `NOT_FOUND`.
68
+ - The agent-cli skill teaches every 0.4.0 feature: the ports factory reading `PortsBase`, array positionals, `force` with `ctx.force`, and `ctx.files`' `read`, `list`, `remove` and `mkdir` with a caught `ALREADY_EXISTS`. Its example `links` gains `bundle <folder> <ids...>`, which writes shortcut files into a new folder and replaces an existing one under `--force`, a `web` port that `links check` uses and whose factory reads `LINKS_TOKEN`, and reads `links.json` through `ctx.files`.
69
+
70
+ ### Fixed
71
+
72
+ - Under a Coding agent, a `PERMISSION_DENIED` on a read (`ctx.files.read`, `list` or `exists`, an unreadable `.env`, a tool `ctx.spawn` cannot run) asks the user to allow reading the path in the sandbox, not to add it to the sandbox's write list.
73
+ - A `$XDG_CONFIG_HOME/<cli>/.env` the Runtime cannot read makes `ctx.secret` fail with `PERMISSION_DENIED` (exit 2) naming the file, with the sandbox hint under an agent, instead of exit 70. A missing `.env` still means no secrets.
74
+
7
75
  ## v0.3.0 (2026-10-19)
8
76
 
9
77
  ### Added