@duyquangnvx/agent-cli 0.0.0-stage → 0.2.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 +34 -0
- package/README.md +10 -2
- package/dist/built-ins.d.ts +4 -0
- package/dist/built-ins.js +20 -0
- package/dist/built-ins.js.map +1 -0
- package/dist/command-spec.d.ts +28 -0
- package/dist/command-spec.js +31 -0
- package/dist/command-spec.js.map +1 -0
- package/dist/confirm.d.ts +7 -0
- package/dist/confirm.js +22 -0
- package/dist/confirm.js.map +1 -0
- package/dist/define.d.ts +132 -0
- package/dist/define.js +10 -0
- package/dist/define.js.map +1 -0
- package/dist/doctor-rows.d.ts +3 -0
- package/dist/doctor-rows.js +115 -0
- package/dist/doctor-rows.js.map +1 -0
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +100 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.js +72 -0
- package/dist/errors.js.map +1 -0
- package/dist/execute.d.ts +21 -0
- package/dist/execute.js +26 -0
- package/dist/execute.js.map +1 -0
- package/dist/flags.d.ts +50 -0
- package/dist/flags.js +59 -0
- package/dist/flags.js.map +1 -0
- package/dist/global-install.d.ts +13 -0
- package/dist/global-install.js +51 -0
- package/dist/global-install.js.map +1 -0
- package/dist/help-json.d.ts +4 -0
- package/dist/help-json.js +44 -0
- package/dist/help-json.js.map +1 -0
- package/dist/help.d.ts +5 -0
- package/dist/help.js +64 -0
- package/dist/help.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +22 -0
- package/dist/input.js +63 -0
- package/dist/input.js.map +1 -0
- package/dist/interrupts.d.ts +18 -0
- package/dist/interrupts.js +118 -0
- package/dist/interrupts.js.map +1 -0
- package/dist/invocation.d.ts +9 -0
- package/dist/invocation.js +37 -0
- package/dist/invocation.js.map +1 -0
- package/dist/list.d.ts +44 -0
- package/dist/list.js +126 -0
- package/dist/list.js.map +1 -0
- package/dist/npm.d.ts +24 -0
- package/dist/npm.js +181 -0
- package/dist/npm.js.map +1 -0
- package/dist/output.d.ts +33 -0
- package/dist/output.js +65 -0
- package/dist/output.js.map +1 -0
- package/dist/parse-input.d.ts +19 -0
- package/dist/parse-input.js +147 -0
- package/dist/parse-input.js.map +1 -0
- package/dist/route.d.ts +25 -0
- package/dist/route.js +75 -0
- package/dist/route.js.map +1 -0
- package/dist/run-cli.d.ts +16 -0
- package/dist/run-cli.js +85 -0
- package/dist/run-cli.js.map +1 -0
- package/dist/run-process.d.ts +2 -0
- package/dist/run-process.js +37 -0
- package/dist/run-process.js.map +1 -0
- package/dist/semver.d.ts +9 -0
- package/dist/semver.js +70 -0
- package/dist/semver.js.map +1 -0
- package/dist/skills.d.ts +11 -0
- package/dist/skills.js +58 -0
- package/dist/skills.js.map +1 -0
- package/dist/style.d.ts +13 -0
- package/dist/style.js +38 -0
- package/dist/style.js.map +1 -0
- package/dist/suggest.d.ts +6 -0
- package/dist/suggest.js +49 -0
- package/dist/suggest.js.map +1 -0
- package/dist/tokenize.d.ts +20 -0
- package/dist/tokenize.js +79 -0
- package/dist/tokenize.js.map +1 -0
- package/dist/update.d.ts +3 -0
- package/dist/update.js +91 -0
- package/dist/update.js.map +1 -0
- package/dist/upgrade-notes.d.ts +9 -0
- package/dist/upgrade-notes.js +41 -0
- package/dist/upgrade-notes.js.map +1 -0
- package/dist/usage.d.ts +5 -0
- package/dist/usage.js +37 -0
- package/dist/usage.js.map +1 -0
- package/package.json +29 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duyquangnvx/create-agent-cli` (the Scaffolder), which are released together at one version, newest first, in the format of [Keep a Changelog 2.0](https://keepachangelog.com/en/2.0.0/). Each change lands under `## Unreleased` as it merges, grouped under `### Added`, `### Changed`, `### Fixed` or `### Removed`. A breaking change starts with `**Breaking:**` and names what breaks: an export, a Cli or Command field, a built-in Command, a flag, a `--json` field, an exit code, the scaffolded template or the skill. A release turns `## Unreleased` into `## vX.Y.Z (YYYY-MM-DD)`.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## v0.2.0 (2026-10-19)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Breaking:** a List Command is declared by its row schema alone: a read Command sets `rows: rowSchema` in place of `output`, and the Runtime adds `--limit`, `--cursor` and `--fields`, passes `limit` and `cursor` to `run` and gives help JSON the page of rows as its output schema. A described or otherwise wrapped row schema keeps `--fields`, and a write or destructive Command with `rows` fails type checking. The exports `pageOf` and `pageOptions` are removed; the agent-cli skill and the scaffolded template use `rows`.
|
|
12
|
+
- **Breaking:** help and `--help --json` list `--limit`, `--cursor` and `--fields` under Standard flags instead of among the Command's options, so help JSON's `options` no longer has `fields`, `limit` or `cursor`. Each entry of help JSON's `standardFlags` carries the JSON Schema of the flag's value as `schema`, such as `--limit`'s positive integer with default 20.
|
|
13
|
+
- **Breaking:** `defineCommand` throws, and type checking fails, when a Command declares an arg or option named like any Standard flag, whether or not that Command gets the flag: `json`, `help`, `h`, `version`, `dry-run` (key `dryRun`), `confirm`, `limit`, `cursor` or `fields`.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `--help` or `-h` after `--` is an argument, as every other word after `--` is, instead of showing help.
|
|
18
|
+
|
|
19
|
+
## v0.1.0 (2026-10-18)
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- The Runtime, `@duyquangnvx/agent-cli`: `defineCli` and `defineCommand` declare Commands with Zod arguments and options and a read, write or destructive Effect, and the Runtime owns parsing, layered help, `--json` output, errors with codes and hints, and exit codes 0, 1 and 2. Write and destructive Commands get `--dry-run` from their Plan, destructive ones a confirmation, and `pageOptions`, `pageOf` and `paginate` page a list. `runCli` runs a CLI in-process for tests; `runProcess` runs it as a bin, which exits once its output is written.
|
|
24
|
+
- The Scaffolder, `@duyquangnvx/create-agent-cli`: `npm create @duyquangnvx/agent-cli <name>` creates a repo for a new Agent CLI with one example Command, its test, a skeleton skill, the gate scripts and a first commit.
|
|
25
|
+
- Every multi-Command CLI has a built-in `doctor`: one row per check (`node`, `install`, `registry`, `skill`, then the CLI's own `doctor` rows), each under its own 10 s deadline, followed by the commands that fix each problem. It exits 1 with `DOCTOR_PROBLEMS` when a row is a problem; `--json` returns the rows as data.
|
|
26
|
+
- Every multi-Command CLI has a built-in `update`, which installs the latest version from the CLI's registry with `npm install --global`, prints the upgrade notes from the new `CHANGELOG.md` for each version it passed, and says to update the skill. `--dry-run` shows the version it would install. Ctrl-C never stops npm halfway: once the install has started it finishes, and `update` fails with `INTERRUPTED` saying the new version is installed. It fails with `NOT_GLOBAL_INSTALL`, `REGISTRY_UNREACHABLE`, `REGISTRY_AUTH`, `NO_LATEST` or `INSTALL_FAILED`.
|
|
27
|
+
- A Cli declares `install` (a global npm install of its package from its registry), `engines` and `doctor` rows, which `doctor` and `update` read.
|
|
28
|
+
- A Command whose `output` is a `pageOf(row)` schema accepts `--fields`, which keeps only the named columns in both text and `--json` output; an unknown name is a usage error listing the valid ones.
|
|
29
|
+
- `--help --json` at any level returns the Commands, arguments, options, Effects and examples as data, with each Command's output JSON Schema from its `output`.
|
|
30
|
+
- Ctrl-C or SIGTERM aborts the Command's `ctx.signal` and the run fails with `INTERRUPTED`; a second signal exits at once with 130 or 143. `ctx.deadline(ms)` returns a signal that aborts with `DEADLINE_EXCEEDED`, `ctx.runToEnd(work)` marks a step the process finishes before it exits, and `runCli` takes a `signal` to cancel a run in-process.
|
|
31
|
+
- A Cli with `build` prints `--version` as `X.Y.Z (sha)`, or `X.Y.Z (sha+dirty)` for a build from an unclean tree.
|
|
32
|
+
- A scaffolded CLI bundles the Runtime and zod with tsdown, which stamps its version and commit into `--version`; both are devDependencies, so its own version alone decides its behaviour.
|
|
33
|
+
- A scaffolded CLI declares a global install from npmjs and releases with `@duyquangnvx/release`, which stamps its skill and checks its `--version` before publishing.
|
|
34
|
+
- The agent-cli skill, which teaches a Coding agent to build an Agent CLI on the Runtime: `npx skills add` installs it from this repo's `release` branch, and it names the Runtime version it describes.
|
package/README.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @duyquangnvx/agent-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Runtime for Agent CLIs: command-line tools whose main caller is an LLM coding agent. You declare Commands with Zod schemas. The Runtime does the parsing, help (as text and as `--help --json`), output, errors with hints, exit codes, `--dry-run` and `--confirm`, pagination, Ctrl-C and deadlines, and the built-in `doctor` and `update`.
|
|
4
|
+
|
|
5
|
+
An Agent CLI bundles the Runtime, so install it as a devDependency next to `zod`. The fastest start is the Scaffolder:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm create @duyquangnvx/agent-cli <name> --description "One line saying what it does"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
See the [repository](https://github.com/duyquangnvx/cli-for-agent) for an overview and the [agent-cli skill](https://github.com/duyquangnvx/cli-for-agent/blob/release/skills/agent-cli/SKILL.md) for the full guide.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Cli, Commands, CommandTreeCli } from "./define.js";
|
|
2
|
+
/** The Commands the Runtime adds to a multi-Command CLI: `update` only when it declares an install. */
|
|
3
|
+
export declare function builtInCommands(cli: CommandTreeCli, bin: string | undefined): Commands;
|
|
4
|
+
export declare function defineCli(cli: Cli): Cli;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { doctorCommand } from "./doctor.js";
|
|
2
|
+
import { updateCommand } from "./update.js";
|
|
3
|
+
/** The Commands the Runtime adds to a multi-Command CLI: `update` only when it declares an install. */
|
|
4
|
+
export function builtInCommands(cli, bin) {
|
|
5
|
+
return {
|
|
6
|
+
doctor: doctorCommand(cli, bin),
|
|
7
|
+
...(cli.install === undefined ? {} : { update: updateCommand(cli, cli.install, bin) }),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function defineCli(cli) {
|
|
11
|
+
if (cli.commands === undefined)
|
|
12
|
+
return cli;
|
|
13
|
+
const { commands } = cli;
|
|
14
|
+
const taken = Object.keys(builtInCommands(cli, undefined)).filter((name) => commands[name] !== undefined);
|
|
15
|
+
if (taken.length > 0) {
|
|
16
|
+
throw new Error(`${cli.name} defines ${taken.join(", ")}, a built-in Command it cannot replace`);
|
|
17
|
+
}
|
|
18
|
+
return cli;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=built-ins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"built-ins.js","sourceRoot":"","sources":["../src/built-ins.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,uGAAuG;AACvG,MAAM,UAAU,eAAe,CAAC,GAAmB,EAAE,GAAuB;IAC1E,OAAO;QACL,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC;QAC/B,GAAG,CAAC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;KACvF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAQ;IAChC,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAC/D,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,SAAS,CACvC,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,CAAC,IAAI,YAAY,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wCAAwC,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type * as z from "zod";
|
|
2
|
+
import { type Command, type Commands, type Example } from "./define.js";
|
|
3
|
+
import { type StandardFlag } from "./flags.js";
|
|
4
|
+
import { type InputField } from "./input.js";
|
|
5
|
+
import { type ListSpec } from "./list.js";
|
|
6
|
+
/** Everything help, help JSON and input parsing know about one Command. */
|
|
7
|
+
export interface CommandSpec {
|
|
8
|
+
readonly kind: "command";
|
|
9
|
+
readonly commandLine: string;
|
|
10
|
+
readonly command: Command;
|
|
11
|
+
readonly description: string;
|
|
12
|
+
readonly effect: Command["effect"];
|
|
13
|
+
readonly examples: readonly Example[];
|
|
14
|
+
readonly args: readonly InputField[];
|
|
15
|
+
/** The options the author declared; Standard flags are kept apart in `standardFlags`. */
|
|
16
|
+
readonly options: readonly InputField[];
|
|
17
|
+
readonly standardFlags: readonly StandardFlag[];
|
|
18
|
+
readonly list: ListSpec | undefined;
|
|
19
|
+
readonly output: z.ZodType | undefined;
|
|
20
|
+
}
|
|
21
|
+
export interface GroupSpec {
|
|
22
|
+
readonly kind: "group";
|
|
23
|
+
readonly commandLine: string;
|
|
24
|
+
readonly commands: Readonly<Record<string, SpecNode>>;
|
|
25
|
+
}
|
|
26
|
+
export type SpecNode = CommandSpec | GroupSpec;
|
|
27
|
+
export declare function commandSpec(commandLine: string, command: Command): CommandSpec;
|
|
28
|
+
export declare function groupSpec(commandLine: string, commands: Commands): GroupSpec;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { isCommand } from "./define.js";
|
|
2
|
+
import { standardFlagsFor } from "./flags.js";
|
|
3
|
+
import { argFields, optionFields } from "./input.js";
|
|
4
|
+
import { listSpec } from "./list.js";
|
|
5
|
+
export function commandSpec(commandLine, command) {
|
|
6
|
+
const list = command.rows === undefined ? undefined : listSpec(command.rows);
|
|
7
|
+
return {
|
|
8
|
+
kind: "command",
|
|
9
|
+
commandLine,
|
|
10
|
+
command,
|
|
11
|
+
description: command.description,
|
|
12
|
+
effect: command.effect,
|
|
13
|
+
examples: command.examples,
|
|
14
|
+
args: argFields(command.args),
|
|
15
|
+
options: optionFields(command.options),
|
|
16
|
+
standardFlags: standardFlagsFor(command.effect, list),
|
|
17
|
+
list,
|
|
18
|
+
output: list?.output ?? command.output,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function groupSpec(commandLine, commands) {
|
|
22
|
+
return {
|
|
23
|
+
kind: "group",
|
|
24
|
+
commandLine,
|
|
25
|
+
commands: Object.fromEntries(Object.entries(commands).map(([key, node]) => [key, specOf(`${commandLine} ${key}`, node)])),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function specOf(commandLine, node) {
|
|
29
|
+
return isCommand(node) ? commandSpec(commandLine, node) : groupSpec(commandLine, node);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=command-spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-spec.js","sourceRoot":"","sources":["../src/command-spec.ts"],"names":[],"mappings":"AACA,OAAO,EAA6C,SAAS,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAqB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,SAAS,EAAmB,YAAY,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAiB,QAAQ,EAAE,MAAM,WAAW,CAAC;AA0BpD,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,OAAgB;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,OAAO;QACL,IAAI,EAAE,SAAS;QACf,WAAW;QACX,OAAO;QACP,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;QAC7B,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC;QACtC,aAAa,EAAE,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;QACrD,IAAI;QACJ,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,OAAO,CAAC,MAAM;KACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,WAAmB,EAAE,QAAkB;IAC/D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,WAAW;QACX,QAAQ,EAAE,MAAM,CAAC,WAAW,CAC1B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,WAAW,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAC5F;KACF,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,WAAmB,EAAE,IAAwB;IAC3D,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Invocation } from "./invocation.js";
|
|
2
|
+
export interface Confirmation {
|
|
3
|
+
readonly invocation: Invocation;
|
|
4
|
+
readonly commandLine: string;
|
|
5
|
+
readonly given: string | undefined;
|
|
6
|
+
}
|
|
7
|
+
export declare function checkConfirmation({ invocation, commandLine, given }: Confirmation, expected: string): void;
|
package/dist/confirm.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { UsageError } from "./errors.js";
|
|
2
|
+
import { CONFIRM_FLAG } from "./flags.js";
|
|
3
|
+
import { commandLineWith } from "./invocation.js";
|
|
4
|
+
export function checkConfirmation({ invocation, commandLine, given }, expected) {
|
|
5
|
+
if (given === expected) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const hint = `to confirm, run \`${commandLineWith(invocation, CONFIRM_FLAG.flag, expected)}\``;
|
|
9
|
+
if (given === undefined) {
|
|
10
|
+
throw new UsageError({
|
|
11
|
+
code: "CONFIRMATION_REQUIRED",
|
|
12
|
+
message: `\`${commandLine}\` is destructive and needs ${CONFIRM_FLAG.flag}`,
|
|
13
|
+
hint,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
throw new UsageError({
|
|
17
|
+
code: "CONFIRMATION_MISMATCH",
|
|
18
|
+
message: `${CONFIRM_FLAG.flag} ${JSON.stringify(given)} does not match the target of \`${commandLine}\``,
|
|
19
|
+
hint,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=confirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../src/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAmB,MAAM,iBAAiB,CAAC;AAQnE,MAAM,UAAU,iBAAiB,CAC/B,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAgB,EAChD,QAAgB;IAEhB,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,qBAAqB,eAAe,CAAC,UAAU,EAAE,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;IAC/F,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,KAAK,WAAW,+BAA+B,YAAY,CAAC,IAAI,EAAE;YAC3E,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,UAAU,CAAC;QACnB,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,GAAG,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,mCAAmC,WAAW,IAAI;QACxG,IAAI;KACL,CAAC,CAAC;AACL,CAAC"}
|
package/dist/define.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type * as z from "zod";
|
|
2
|
+
import { type StandardFlagName } from "./flags.js";
|
|
3
|
+
import type { Page, PageInput, RowSchema } from "./list.js";
|
|
4
|
+
import type { Style } from "./style.js";
|
|
5
|
+
export type Env = Readonly<Record<string, string | undefined>>;
|
|
6
|
+
export interface Example {
|
|
7
|
+
readonly command: string;
|
|
8
|
+
readonly note: string;
|
|
9
|
+
}
|
|
10
|
+
export interface Context {
|
|
11
|
+
readonly cwd: string;
|
|
12
|
+
readonly env: Env;
|
|
13
|
+
/** Writes a plain line to stderr, for long work; it never reaches stdout. */
|
|
14
|
+
progress(message: string): void;
|
|
15
|
+
/** Aborts when the run is interrupted (Ctrl-C); pass it to `fetch` and child processes. */
|
|
16
|
+
readonly signal: AbortSignal;
|
|
17
|
+
/** Returns a signal that also aborts, with `DEADLINE_EXCEEDED`, once `ms` have passed. */
|
|
18
|
+
deadline(ms: number): AbortSignal;
|
|
19
|
+
/** Runs a step that must not stop halfway: the run waits for it even when interrupted. */
|
|
20
|
+
runToEnd<T>(work: () => Promise<T>): Promise<T>;
|
|
21
|
+
}
|
|
22
|
+
export type InputSchema = z.ZodObject;
|
|
23
|
+
export type NoInput = z.ZodObject<Record<never, never>>;
|
|
24
|
+
type OutputOf<Schema extends InputSchema> = keyof Schema["shape"] extends never ? unknown : z.output<Schema>;
|
|
25
|
+
type Flatten<T> = {
|
|
26
|
+
[K in keyof T]: T[K];
|
|
27
|
+
};
|
|
28
|
+
export type Input<Args extends InputSchema, Options extends InputSchema> = Flatten<OutputOf<Args> & OutputOf<Options>>;
|
|
29
|
+
interface CommandBase<Args extends InputSchema, Options extends InputSchema> {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly description: string;
|
|
32
|
+
readonly examples: readonly Example[];
|
|
33
|
+
readonly args?: Args;
|
|
34
|
+
readonly options?: Options;
|
|
35
|
+
}
|
|
36
|
+
interface UnpagedCommand<Args extends InputSchema, Options extends InputSchema> extends CommandBase<Args, Options> {
|
|
37
|
+
readonly output?: z.ZodType;
|
|
38
|
+
readonly rows?: never;
|
|
39
|
+
}
|
|
40
|
+
export interface ReadCommand<Args extends InputSchema, Options extends InputSchema, Data> extends UnpagedCommand<Args, Options> {
|
|
41
|
+
readonly effect: "read";
|
|
42
|
+
run(input: Input<Args, Options>, ctx: Context): Data | Promise<Data>;
|
|
43
|
+
render(data: Data, style: Style): string;
|
|
44
|
+
}
|
|
45
|
+
/** A read Command whose data is a page of rows; the Runtime adds `--limit`, `--cursor` and `--fields`. */
|
|
46
|
+
export interface ListCommand<Args extends InputSchema, Options extends InputSchema, Row extends RowSchema> extends CommandBase<Args, Options> {
|
|
47
|
+
readonly effect: "read";
|
|
48
|
+
/** The schema of one row; the page's output schema is derived from it. */
|
|
49
|
+
readonly rows: Row;
|
|
50
|
+
readonly output?: never;
|
|
51
|
+
run(input: Flatten<OutputOf<Args> & OutputOf<Options> & PageInput>, ctx: Context): Page<z.output<Row>> | Promise<Page<z.output<Row>>>;
|
|
52
|
+
render(page: Page<z.output<Row>>, style: Style): string;
|
|
53
|
+
}
|
|
54
|
+
/** A Command that changes something: `plan` has no side effects, `apply` performs the Plan. */
|
|
55
|
+
interface PlannedCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data> extends UnpagedCommand<Args, Options> {
|
|
56
|
+
plan(input: Input<Args, Options>, ctx: Context): Plan | Promise<Plan>;
|
|
57
|
+
apply(plan: Plan, ctx: Context): Data | Promise<Data>;
|
|
58
|
+
render(data: Data, style: Style): string;
|
|
59
|
+
renderPlan(plan: Plan, style: Style): string;
|
|
60
|
+
}
|
|
61
|
+
export interface WriteCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data> extends PlannedCommand<Args, Options, Plan, Data> {
|
|
62
|
+
readonly effect: "write";
|
|
63
|
+
}
|
|
64
|
+
export interface DestructiveCommand<Args extends InputSchema, Options extends InputSchema, Plan, Data> extends PlannedCommand<Args, Options, Plan, Data> {
|
|
65
|
+
readonly effect: "destructive";
|
|
66
|
+
/** The value the caller must repeat as `--confirm`, naming the target the Plan changes. */
|
|
67
|
+
confirm(plan: Plan): string;
|
|
68
|
+
}
|
|
69
|
+
export type Command = ReadCommand<InputSchema, InputSchema, unknown> | ListCommand<InputSchema, InputSchema, RowSchema> | WriteCommand<InputSchema, InputSchema, unknown, unknown> | DestructiveCommand<InputSchema, InputSchema, unknown, unknown>;
|
|
70
|
+
export interface Commands {
|
|
71
|
+
readonly [key: string]: Command | Commands;
|
|
72
|
+
}
|
|
73
|
+
interface CliBase {
|
|
74
|
+
readonly name: string;
|
|
75
|
+
readonly version: string;
|
|
76
|
+
/** The commit the build was made from, stamped by the bundler; `--version` prints it. */
|
|
77
|
+
readonly build?: {
|
|
78
|
+
readonly sha: string;
|
|
79
|
+
readonly dirty: boolean;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Where an Agent CLI is published and how it is installed; `doctor` and `update` read it. */
|
|
83
|
+
export interface GlobalInstall {
|
|
84
|
+
readonly mode: "global";
|
|
85
|
+
readonly package: string;
|
|
86
|
+
readonly registry: string;
|
|
87
|
+
}
|
|
88
|
+
export interface DoctorFinding {
|
|
89
|
+
readonly state: "ok" | "problem";
|
|
90
|
+
readonly message: string;
|
|
91
|
+
/** The command that fixes the problem. */
|
|
92
|
+
readonly fix?: string;
|
|
93
|
+
}
|
|
94
|
+
/** A row the CLI adds to `doctor`, for a system it depends on. */
|
|
95
|
+
export interface DoctorCheck {
|
|
96
|
+
readonly name: string;
|
|
97
|
+
/** `ctx.signal` aborts at the 10 s deadline. A thrown error is reported as this row's problem. */
|
|
98
|
+
run(ctx: Context): DoctorFinding | Promise<DoctorFinding>;
|
|
99
|
+
}
|
|
100
|
+
/** An Agent CLI whose Commands are reached by name, as in `tool voices list`. */
|
|
101
|
+
export interface CommandTreeCli extends CliBase {
|
|
102
|
+
readonly commands: Commands;
|
|
103
|
+
readonly command?: never;
|
|
104
|
+
/** Absent for a CLI that is not published: `doctor` then checks no install or registry. */
|
|
105
|
+
readonly install?: GlobalInstall;
|
|
106
|
+
/** The package.json `engines`; `doctor` checks `node` when it is `>=X[.Y[.Z]]`. */
|
|
107
|
+
readonly engines?: {
|
|
108
|
+
readonly node?: string;
|
|
109
|
+
};
|
|
110
|
+
readonly doctor?: readonly DoctorCheck[];
|
|
111
|
+
}
|
|
112
|
+
/** An Agent CLI that is one Command, run with no Command name, as in `tool <args>`. */
|
|
113
|
+
export interface SingleCommandCli extends CliBase {
|
|
114
|
+
readonly command: Command;
|
|
115
|
+
readonly commands?: never;
|
|
116
|
+
}
|
|
117
|
+
export type Cli = CommandTreeCli | SingleCommandCli;
|
|
118
|
+
type Kebab<Key extends string> = Key extends `${infer Head}${infer Tail}` ? `${Head extends Lowercase<Head> ? Head : `-${Lowercase<Head>}`}${Kebab<Tail>}` : Key;
|
|
119
|
+
type Taken<Schema extends InputSchema> = Extract<Kebab<Extract<keyof Schema["shape"], string>>, StandardFlagName>;
|
|
120
|
+
type Unreserved<Schema extends InputSchema> = [Taken<Schema>] extends [never] ? unknown : {
|
|
121
|
+
readonly standardFlagDeclared: Taken<Schema>;
|
|
122
|
+
};
|
|
123
|
+
interface WithoutStandardFlags<Args extends InputSchema, Options extends InputSchema> {
|
|
124
|
+
readonly args?: Unreserved<Args>;
|
|
125
|
+
readonly options?: Unreserved<Options>;
|
|
126
|
+
}
|
|
127
|
+
export declare function defineCommand<Row extends RowSchema, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: ListCommand<Args, Options, Row> & WithoutStandardFlags<Args, Options>): ListCommand<Args, Options, Row>;
|
|
128
|
+
export declare function defineCommand<Data, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: ReadCommand<Args, Options, Data> & WithoutStandardFlags<Args, Options>): ReadCommand<Args, Options, Data>;
|
|
129
|
+
export declare function defineCommand<Plan, Data, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: WriteCommand<Args, Options, Plan, Data> & WithoutStandardFlags<Args, Options>): WriteCommand<Args, Options, Plan, Data>;
|
|
130
|
+
export declare function defineCommand<Plan, Data, Args extends InputSchema = NoInput, Options extends InputSchema = NoInput>(command: DestructiveCommand<Args, Options, Plan, Data> & WithoutStandardFlags<Args, Options>): DestructiveCommand<Args, Options, Plan, Data>;
|
|
131
|
+
export declare function isCommand(node: Command | Commands): node is Command;
|
|
132
|
+
export {};
|
package/dist/define.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { checkNoStandardFlags } from "./flags.js";
|
|
2
|
+
export function defineCommand(command) {
|
|
3
|
+
checkNoStandardFlags(command);
|
|
4
|
+
return command;
|
|
5
|
+
}
|
|
6
|
+
// A group's values are always objects, so a string `effect` can only belong to a Command.
|
|
7
|
+
export function isCommand(node) {
|
|
8
|
+
return typeof node.effect === "string";
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=define.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,YAAY,CAAC;AAgNzE,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,SAAS,CAAC,IAAwB;IAChD,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { CliError } from "./errors.js";
|
|
2
|
+
import { globalInstallOf } from "./global-install.js";
|
|
3
|
+
import { installCommandLine, latestVersion, npmContext } from "./npm.js";
|
|
4
|
+
import { engineMinimum, meetsMinimum, standingOf } from "./semver.js";
|
|
5
|
+
import { skillCopies, skillUpdateCommand } from "./skills.js";
|
|
6
|
+
function ok(message) {
|
|
7
|
+
return { state: "ok", message };
|
|
8
|
+
}
|
|
9
|
+
function nodeRow(cli) {
|
|
10
|
+
return {
|
|
11
|
+
name: "node",
|
|
12
|
+
run: () => {
|
|
13
|
+
const found = process.versions.node;
|
|
14
|
+
const range = cli.engines?.node;
|
|
15
|
+
if (range === undefined) {
|
|
16
|
+
return ok(`Node ${found}; ${cli.name} declares no engines.node to check it against`);
|
|
17
|
+
}
|
|
18
|
+
const minimum = engineMinimum(range);
|
|
19
|
+
if (minimum === undefined) {
|
|
20
|
+
return {
|
|
21
|
+
state: "problem",
|
|
22
|
+
message: `${cli.name} declares engines.node "${range}", which doctor cannot read: only >=X.Y.Z is supported`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (meetsMinimum(found, minimum)) {
|
|
26
|
+
return ok(`Node ${found} meets ${range}`);
|
|
27
|
+
}
|
|
28
|
+
const major = minimum[0];
|
|
29
|
+
return {
|
|
30
|
+
state: "problem",
|
|
31
|
+
message: `${cli.name} runs on Node ${found}, older than the ${range} it needs; install Node ${minimum.join(".")} or newer and make it the default`,
|
|
32
|
+
fix: `nvm install ${major} && nvm alias default ${major}`,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function installRow(cli, bin) {
|
|
38
|
+
return {
|
|
39
|
+
name: "install",
|
|
40
|
+
run: async (ctx) => {
|
|
41
|
+
const { install } = cli;
|
|
42
|
+
if (install === undefined) {
|
|
43
|
+
return ok(`${cli.name} declares no install, so it has no \`update\``);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const copy = await globalInstallOf(bin, npmContext(cli, install, ctx));
|
|
47
|
+
return ok(`a global npm install under ${copy.prefix}`);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof CliError && error.code === "NOT_GLOBAL_INSTALL") {
|
|
51
|
+
return { state: "problem", message: error.message, fix: installCommandLine(install) };
|
|
52
|
+
}
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function registryRow(cli) {
|
|
59
|
+
return {
|
|
60
|
+
name: "registry",
|
|
61
|
+
run: async (ctx) => {
|
|
62
|
+
const { install } = cli;
|
|
63
|
+
if (install === undefined) {
|
|
64
|
+
return ok(`${cli.name} declares no registry to compare this build with`);
|
|
65
|
+
}
|
|
66
|
+
const latest = await latestVersion(npmContext(cli, install, ctx));
|
|
67
|
+
const standing = standingOf(cli.version, latest);
|
|
68
|
+
const head = `${install.registry} has ${latest} as latest: this build`;
|
|
69
|
+
if (standing === "behind") {
|
|
70
|
+
return {
|
|
71
|
+
state: "problem",
|
|
72
|
+
message: `${head} (${cli.version}) is older`,
|
|
73
|
+
fix: `${cli.name} update`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return ok(`${head} is ${standing === "current" ? "current" : `newer (${cli.version})`}`);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const SKILL_STATE_NOTES = {
|
|
81
|
+
current: "the same as this build",
|
|
82
|
+
behind: "older than this build",
|
|
83
|
+
ahead: "newer than this build",
|
|
84
|
+
unversioned: "states no metadata.version",
|
|
85
|
+
unreadable: "could not be read",
|
|
86
|
+
};
|
|
87
|
+
function describeCopy({ path, version, state }) {
|
|
88
|
+
return `${path}${version === undefined ? "" : ` ${version}`} (${SKILL_STATE_NOTES[state]})`;
|
|
89
|
+
}
|
|
90
|
+
function skillFix(cli, copies) {
|
|
91
|
+
if (copies.some(({ state }) => state === "behind" || state === "unversioned")) {
|
|
92
|
+
return skillUpdateCommand(cli.name);
|
|
93
|
+
}
|
|
94
|
+
return copies.some(({ state }) => state === "ahead") ? `${cli.name} update` : undefined;
|
|
95
|
+
}
|
|
96
|
+
// A skill describes the CLI but never stops it working, so this row is always ok.
|
|
97
|
+
function skillRow(cli) {
|
|
98
|
+
return {
|
|
99
|
+
name: "skill",
|
|
100
|
+
run: async (ctx) => {
|
|
101
|
+
const copies = await skillCopies(cli.name, cli.version, ctx.cwd, ctx.env);
|
|
102
|
+
if (copies.length === 0) {
|
|
103
|
+
return ok(`no copy of the ${cli.name} skill is installed`);
|
|
104
|
+
}
|
|
105
|
+
const fix = skillFix(cli, copies);
|
|
106
|
+
const message = copies.map(describeCopy).join("; ");
|
|
107
|
+
return fix === undefined ? ok(message) : { state: "ok", message, fix };
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** The rows every `doctor` reports first, in this order. */
|
|
112
|
+
export function fixedRows(cli, bin) {
|
|
113
|
+
return [nodeRow(cli), installRow(cli, bin), registryRow(cli), skillRow(cli)];
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=doctor-rows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-rows.js","sourceRoot":"","sources":["../src/doctor-rows.ts"],"names":[],"mappings":"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"}
|
package/dist/doctor.d.ts
ADDED
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { defineCommand, } from "./define.js";
|
|
3
|
+
import { fixedRows } from "./doctor-rows.js";
|
|
4
|
+
import { CliError, FailureWithData, messageOf } from "./errors.js";
|
|
5
|
+
const PROBE_DEADLINE_MS = 10_000;
|
|
6
|
+
const MS_PER_SECOND = 1000;
|
|
7
|
+
const MIN_LABEL_WIDTH = 8;
|
|
8
|
+
const rowSchema = z.object({
|
|
9
|
+
check: z.string(),
|
|
10
|
+
state: z.enum(["ok", "problem"]),
|
|
11
|
+
message: z.string(),
|
|
12
|
+
fix: z.string().optional(),
|
|
13
|
+
});
|
|
14
|
+
const reportSchema = z.object({
|
|
15
|
+
rows: z.array(rowSchema),
|
|
16
|
+
next: z.array(z.string()),
|
|
17
|
+
});
|
|
18
|
+
function untilAborted(signal) {
|
|
19
|
+
return new Promise((_resolve, reject) => {
|
|
20
|
+
if (signal.aborted) {
|
|
21
|
+
reject(signal.reason);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function problemOf(check, error, { signal, deadline }) {
|
|
28
|
+
if (!signal.aborted && deadline.aborted && error === deadline.reason) {
|
|
29
|
+
return {
|
|
30
|
+
state: "problem",
|
|
31
|
+
message: `${check.name} did not answer within ${PROBE_DEADLINE_MS / MS_PER_SECOND} s`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (error instanceof CliError) {
|
|
35
|
+
return { state: "problem", message: `${error.message}; ${error.hint}` };
|
|
36
|
+
}
|
|
37
|
+
return { state: "problem", message: `the ${check.name} row hit a bug: ${messageOf(error)}` };
|
|
38
|
+
}
|
|
39
|
+
// Racing the deadline ends a row whose check ignores its signal.
|
|
40
|
+
async function rowOf(check, ctx) {
|
|
41
|
+
const signal = ctx.deadline(PROBE_DEADLINE_MS);
|
|
42
|
+
let finding;
|
|
43
|
+
try {
|
|
44
|
+
finding = await Promise.race([
|
|
45
|
+
Promise.resolve().then(() => check.run({ ...ctx, signal })),
|
|
46
|
+
untilAborted(signal),
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
finding = problemOf(check, error, { signal: ctx.signal, deadline: signal });
|
|
51
|
+
}
|
|
52
|
+
return { check: check.name, ...finding };
|
|
53
|
+
}
|
|
54
|
+
function nextOf(rows) {
|
|
55
|
+
return [...new Set(rows.flatMap(({ fix }) => (fix === undefined ? [] : [fix])))];
|
|
56
|
+
}
|
|
57
|
+
function render({ rows, next }, style) {
|
|
58
|
+
const width = Math.max(MIN_LABEL_WIDTH, ...rows.map(({ check }) => check.length)) + 2;
|
|
59
|
+
const stateWidth = Math.max(...rows.map(({ state }) => state.length)) + 2;
|
|
60
|
+
const lines = rows.map(({ check, state, message }) => {
|
|
61
|
+
const label = state === "problem" ? style.red(state) : state;
|
|
62
|
+
return `${check.padEnd(width)}${label}${" ".repeat(stateWidth - state.length)}${message}`;
|
|
63
|
+
});
|
|
64
|
+
return [...lines, ...next.map((fix) => `${"next".padEnd(width)}${fix}`)].join("\n");
|
|
65
|
+
}
|
|
66
|
+
function failure(cliName, report) {
|
|
67
|
+
const problems = report.rows.filter(({ state }) => state === "problem").map(({ check }) => check);
|
|
68
|
+
return new FailureWithData({
|
|
69
|
+
code: "DOCTOR_PROBLEMS",
|
|
70
|
+
message: `${cliName} doctor found ${problems.length === 1 ? "a problem" : `${problems.length} problems`}: ${problems.join(", ")}`,
|
|
71
|
+
hint: report.next.length === 0
|
|
72
|
+
? "each problem row says what is wrong"
|
|
73
|
+
: "run the commands under next, then run doctor again",
|
|
74
|
+
retryable: false,
|
|
75
|
+
}, report);
|
|
76
|
+
}
|
|
77
|
+
/** The built-in `doctor`: the fixed rows, then the CLI's own, each under its own deadline. */
|
|
78
|
+
export function doctorCommand(cli, bin) {
|
|
79
|
+
return defineCommand({
|
|
80
|
+
name: "doctor",
|
|
81
|
+
description: "check the setup and say what fixes each problem",
|
|
82
|
+
examples: [
|
|
83
|
+
{ command: `${cli.name} doctor`, note: "check the setup and list the commands that fix it" },
|
|
84
|
+
{ command: `${cli.name} doctor --json`, note: "get the rows and fixes as data" },
|
|
85
|
+
],
|
|
86
|
+
effect: "read",
|
|
87
|
+
output: reportSchema,
|
|
88
|
+
run: async (_input, ctx) => {
|
|
89
|
+
const checks = [...fixedRows(cli, bin), ...(cli.doctor ?? [])];
|
|
90
|
+
const rows = await Promise.all(checks.map((check) => rowOf(check, ctx)));
|
|
91
|
+
const report = { rows, next: nextOf(rows) };
|
|
92
|
+
if (rows.some(({ state }) => state === "problem")) {
|
|
93
|
+
throw failure(cli.name, report);
|
|
94
|
+
}
|
|
95
|
+
return report;
|
|
96
|
+
},
|
|
97
|
+
render,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Env } from "./define.js";
|
|
2
|
+
import type { Style } from "./style.js";
|
|
3
|
+
export interface CliErrorDetails {
|
|
4
|
+
readonly code: string;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
readonly hint: string;
|
|
7
|
+
readonly retryable: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class CliError extends Error {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly hint: string;
|
|
12
|
+
readonly retryable: boolean;
|
|
13
|
+
constructor({ code, message, hint, retryable }: CliErrorDetails, options?: ErrorOptions);
|
|
14
|
+
}
|
|
15
|
+
export type UsageErrorDetails = Omit<CliErrorDetails, "retryable">;
|
|
16
|
+
/** The caller called the Agent CLI wrong; retrying the same call can't succeed. */
|
|
17
|
+
export declare class UsageError extends CliError {
|
|
18
|
+
constructor(details: UsageErrorDetails);
|
|
19
|
+
}
|
|
20
|
+
export interface ErrorFormat {
|
|
21
|
+
readonly cliName: string;
|
|
22
|
+
readonly json: boolean;
|
|
23
|
+
readonly env: Env;
|
|
24
|
+
readonly style: Style;
|
|
25
|
+
}
|
|
26
|
+
export declare function isMissingFile(error: unknown): boolean;
|
|
27
|
+
export declare function debugVariable(cliName: string): string;
|
|
28
|
+
export declare function messageOf(error: unknown): string;
|
|
29
|
+
export declare function formatRuntimeError(error: unknown, format: ErrorFormat): string;
|
|
30
|
+
/** A failure that still prints the Command's data on stdout, as `doctor` does with its rows. */
|
|
31
|
+
export declare class FailureWithData extends CliError {
|
|
32
|
+
readonly data: unknown;
|
|
33
|
+
constructor(details: CliErrorDetails, data: unknown);
|
|
34
|
+
}
|