@sdxc/cli 0.0.0-stage → 2026.10.20

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.
Files changed (57) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +376 -2
  3. package/dist/argument.d.ts +120 -0
  4. package/dist/argument.js +104 -0
  5. package/dist/cli.d.ts +93 -0
  6. package/dist/cli.js +229 -0
  7. package/dist/command.d.ts +139 -0
  8. package/dist/command.js +79 -0
  9. package/dist/context.d.ts +138 -0
  10. package/dist/context.js +175 -0
  11. package/dist/describe.d.ts +72 -0
  12. package/dist/describe.js +88 -0
  13. package/dist/errors.d.ts +67 -0
  14. package/dist/errors.js +82 -0
  15. package/dist/exec.d.ts +83 -0
  16. package/dist/exec.js +136 -0
  17. package/dist/flag.d.ts +175 -0
  18. package/dist/flag.js +91 -0
  19. package/dist/handler.d.ts +73 -0
  20. package/dist/handler.js +30 -0
  21. package/dist/help.d.ts +18 -0
  22. package/dist/help.js +141 -0
  23. package/dist/index.d.ts +36 -0
  24. package/dist/index.js +20 -0
  25. package/dist/interactions.d.ts +27 -0
  26. package/dist/interactions.js +373 -0
  27. package/dist/keys.d.ts +49 -0
  28. package/dist/keys.js +146 -0
  29. package/dist/lazy.d.ts +29 -0
  30. package/dist/lazy.js +23 -0
  31. package/dist/middleware.d.ts +78 -0
  32. package/dist/middleware.js +9 -0
  33. package/dist/parse.d.ts +38 -0
  34. package/dist/parse.js +225 -0
  35. package/dist/prompt.d.ts +153 -0
  36. package/dist/prompt.js +53 -0
  37. package/dist/resolve.d.ts +27 -0
  38. package/dist/resolve.js +294 -0
  39. package/dist/schema.d.ts +27 -0
  40. package/dist/schema.js +34 -0
  41. package/dist/screen.d.ts +42 -0
  42. package/dist/screen.js +114 -0
  43. package/dist/session.d.ts +84 -0
  44. package/dist/session.js +219 -0
  45. package/dist/style.d.ts +50 -0
  46. package/dist/style.js +72 -0
  47. package/dist/suggest.d.ts +15 -0
  48. package/dist/suggest.js +50 -0
  49. package/dist/task.d.ts +47 -0
  50. package/dist/task.js +213 -0
  51. package/dist/terminal.d.ts +21 -0
  52. package/dist/terminal.js +101 -0
  53. package/dist/test.d.ts +89 -0
  54. package/dist/test.js +140 -0
  55. package/dist/tree.d.ts +58 -0
  56. package/dist/tree.js +97 -0
  57. package/package.json +40 -5
@@ -0,0 +1,175 @@
1
+ /**
2
+ * The context a command's middleware and handler receive: its parsed arguments and flags, the
3
+ * process it runs in (working directory, environment, output, prompts, tasks, child processes)
4
+ * and the values middleware published, readable by key or as installed properties.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ /**
10
+ * One command's run. A test builds one through `invoke()` from `@sdxc/cli/test`; a middleware
11
+ * publishes services with `set()`, which a handler reads as `ctx.<property>`.
12
+ *
13
+ * @example ctx.set(Database, connect(ctx.env.DATABASE_URL), { property: "db" });
14
+ */
15
+ export class CommandContext {
16
+ /**
17
+ * The arguments the run was given, exactly as `cli.run` received them: the command's names,
18
+ * flags and values before parsing, for logging a run or starting it again.
19
+ */
20
+ argv;
21
+ /** The command's names after the program, `["ui", "add"]` for `sdxc ui add`. */
22
+ path;
23
+ /** The positional arguments, parsed, prompted for, or defaulted. */
24
+ arguments;
25
+ /** The flags, parsed, prompted for, or defaulted. */
26
+ flags;
27
+ cwd;
28
+ env;
29
+ /** Styling for stdout; `style.stderr` styles stderr, each off for a stream that is not a terminal. */
30
+ style;
31
+ /** Aborts on Ctrl+C. */
32
+ signal;
33
+ /**
34
+ * Questions a handler asks itself. Each takes a `default`, which answers without a
35
+ * terminal, so the command stays runnable in CI.
36
+ */
37
+ prompt;
38
+ /**
39
+ * Runs async work under a spinner, answering a `Result`; the label is optional.
40
+ *
41
+ * @example let posts = await ctx.task("Fetching posts", (task) => fetch(url, { signal: task.signal }));
42
+ */
43
+ task;
44
+ /**
45
+ * What the command was piped. `stdin.isTTY` is true at a terminal, where `text()` would wait
46
+ * for the user to end input, so a handler reads it only when `isTTY` is false.
47
+ *
48
+ * @example let sql = ctx.flags.execute ?? (ctx.stdin.isTTY ? undefined : await ctx.stdin.text());
49
+ */
50
+ stdin;
51
+ #session;
52
+ #values = new Map();
53
+ #execEnv = {};
54
+ /** @param init - The parsed command and the run it belongs to */
55
+ constructor(init) {
56
+ this.argv = init.argv;
57
+ this.path = init.path;
58
+ this.arguments = init.arguments;
59
+ this.flags = init.flags;
60
+ this.cwd = init.session.cwd;
61
+ this.env = init.session.env;
62
+ this.style = init.session.style;
63
+ this.signal = init.session.signal;
64
+ this.prompt = init.prompt;
65
+ this.task = init.session.tasks.task;
66
+ this.stdin = createStdin(init.session.stdin);
67
+ this.#session = init.session;
68
+ }
69
+ /** Writes to stdout, above any running task. */
70
+ stdout = (text) => {
71
+ this.#session.screen.write(this.#session.stdout, text);
72
+ };
73
+ /** Writes to stderr, above any running task. */
74
+ stderr = (text) => {
75
+ this.#session.screen.write(this.#session.stderr, text);
76
+ };
77
+ /**
78
+ * Runs a program with no shell, answering its exit code and output, or an `ExecError` when
79
+ * it fails to start or exits non-zero. `"inherit"` and `"stderr"` output pauses any spinner
80
+ * until the child exits.
81
+ *
82
+ * @param command - The program and its arguments, passed through exactly
83
+ * @example let status = await ctx.exec(["git", "status", "--short"], { stdio: "collect" });
84
+ */
85
+ exec(command, options) {
86
+ return this.#session.exec(command, {
87
+ ...options,
88
+ env: { ...this.#execEnv, ...options?.env },
89
+ });
90
+ }
91
+ /**
92
+ * Adds variables every child `exec` starts with, over the run's `env` and under a call's own
93
+ * `options.env`, so a middleware hands children what it knows (a trace parent) and a caller
94
+ * still overrides it. An `undefined` value removes the variable from children.
95
+ *
96
+ * @param variables - Merged over what earlier calls added
97
+ * @example ctx.setExecEnv({ TRACEPARENT: toTraceParent(trace) });
98
+ */
99
+ setExecEnv(variables) {
100
+ this.#execEnv = { ...this.#execEnv, ...variables };
101
+ }
102
+ /**
103
+ * Reads a value some middleware published.
104
+ *
105
+ * @param key - The context key to read
106
+ * @returns Its value, the key's default, or `undefined`
107
+ */
108
+ get(key) {
109
+ if (this.#values.has(key))
110
+ return this.#values.get(key);
111
+ return key.defaultValue;
112
+ }
113
+ /**
114
+ * Reads a value an earlier middleware must have published. A middleware sees the bare
115
+ * context, without the properties earlier ones installed, so this is how it reads them
116
+ * without an `undefined` it has no answer for; a missing value fails the run with exit 1.
117
+ *
118
+ * @param key - The context key to read
119
+ * @throws Error When nothing published a value for the key
120
+ * @example let db = ctx.require(Database);
121
+ */
122
+ require(key) {
123
+ let value = this.get(key);
124
+ if (value === undefined || value === null) {
125
+ throw new Error(`${this.path.join(" ")} read a context key that no middleware published`);
126
+ }
127
+ return value;
128
+ }
129
+ /**
130
+ * Whether a value has been published for a key.
131
+ *
132
+ * @param key - The context key to check
133
+ */
134
+ has(key) {
135
+ return this.#values.has(key);
136
+ }
137
+ /**
138
+ * Publishes a value for a key, optionally installing it as a read-only property so handlers
139
+ * read `ctx.db` rather than `ctx.get(Database)`.
140
+ *
141
+ * @param key - The context key to write
142
+ * @param value - The value to publish
143
+ * @param options - The property name to install it under
144
+ */
145
+ set(key, value, options) {
146
+ this.#values.set(key, value);
147
+ if (options === undefined)
148
+ return;
149
+ Object.defineProperty(this, options.property, {
150
+ value,
151
+ configurable: true,
152
+ enumerable: true,
153
+ writable: false,
154
+ });
155
+ }
156
+ }
157
+ /** A reader over `input` that reads it to the end at most once. */
158
+ function createStdin(input) {
159
+ let reading;
160
+ return {
161
+ isTTY: input.isTTY === true,
162
+ text() {
163
+ reading ??= new Promise((resolve, reject) => {
164
+ let chunks = [];
165
+ input.on("data", (chunk) => {
166
+ chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8"));
167
+ });
168
+ input.once("end", () => resolve(chunks.join("")));
169
+ input.once("error", reject);
170
+ input.resume();
171
+ });
172
+ return reading;
173
+ },
174
+ };
175
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The contract as plain data: every command with its arguments, flags and examples, the same
3
+ * facts the help prints. `--help --json` prints it, and a tool reads it to check what a script,
4
+ * a guide or an agent passes against what the program accepts.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import type { FlagKind } from "./flag.js";
10
+ import type { Entry } from "./tree.js";
11
+ /** One argument of a command, in the order the command line takes them. */
12
+ export interface ArgumentDescription {
13
+ name: string;
14
+ description: string | null;
15
+ /** A value is needed; a missing one is asked for on a terminal when the argument prompts. */
16
+ required: boolean;
17
+ /** Takes every remaining positional. */
18
+ variadic: boolean;
19
+ /** Takes every token after the command's name unparsed, flags included. */
20
+ rest: boolean;
21
+ /** The default, when one is declared. */
22
+ default?: unknown;
23
+ }
24
+ /** One flag of a command, or one every command answers to (`builtin`). */
25
+ export interface FlagDescription {
26
+ /** The long name, written `--<name>`. */
27
+ name: string;
28
+ aliases: string[];
29
+ /** The one-character alias, written `-<short>`. */
30
+ short: string | null;
31
+ kind: FlagKind;
32
+ /** A boolean's `--no-<name>` spelling is accepted. */
33
+ negatable: boolean;
34
+ /** What the help shows in `<…>` after the flag; `null` for a boolean. */
35
+ value: string | null;
36
+ /** The flag may appear bare or with `=value`. */
37
+ optionalValue: boolean;
38
+ /** One value holds several, separated by commas. */
39
+ list: boolean;
40
+ /** Repeating the flag collects every value. */
41
+ repeatable: boolean;
42
+ required: boolean;
43
+ description: string | null;
44
+ /** Answered by every command the program runs: `--help`, `--version`, `--no-progress`. */
45
+ builtin: boolean;
46
+ /** The default, when one is declared. */
47
+ default?: unknown;
48
+ }
49
+ /** One command or group, with every visible member below it. */
50
+ export interface CommandDescription {
51
+ /** The words typed after the program to reach it; empty for the root. */
52
+ path: string[];
53
+ /** The program name and the path, as the help prints it. */
54
+ title: string;
55
+ description: string | null;
56
+ arguments: ArgumentDescription[];
57
+ flags: FlagDescription[];
58
+ /** Example command lines, each starting with the program name. */
59
+ examples: string[];
60
+ /** Members of a group by name; empty for a command. */
61
+ commands: CommandDescription[];
62
+ }
63
+ /** The program's own facts the description includes beside the contract. */
64
+ export interface DescribeProgram {
65
+ version: string | undefined;
66
+ description: string | undefined;
67
+ }
68
+ /**
69
+ * Describes `entry` and every visible member under it. Hidden commands and flags stay out, as
70
+ * they do from the help.
71
+ */
72
+ export declare function describeEntry(entry: Entry, program: DescribeProgram): CommandDescription;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The contract as plain data: every command with its arguments, flags and examples, the same
3
+ * facts the help prints. `--help --json` prints it, and a tool reads it to check what a script,
4
+ * a guide or an agent passes against what the program accepts.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import { groupSpec, isGroup } from "./command.js";
10
+ /**
11
+ * Describes `entry` and every visible member under it. Hidden commands and flags stay out, as
12
+ * they do from the help.
13
+ */
14
+ export function describeEntry(entry, program) {
15
+ let node = entry.node;
16
+ let spec = isGroup(node) ? groupSpec(node) : node;
17
+ let description = spec.description ?? (entry.parent === undefined ? program.description : undefined);
18
+ let flags = entry.flags.filter(({ spec }) => !spec.hidden).map(({ spec }) => describeFlag(spec));
19
+ if (entry.tasks)
20
+ flags.push(builtin("progress", "Print tasks without animation", true));
21
+ if (program.version !== undefined && entry.parent === undefined) {
22
+ flags.push(builtin("version", "Print the version", false));
23
+ }
24
+ flags.push({ ...builtin("help", "Show this help", false), short: "h" });
25
+ let programName = entry.title.split(" ")[0];
26
+ let commands = [];
27
+ for (let child of entry.children.values()) {
28
+ let childSpec = isGroup(child.node) ? groupSpec(child.node) : child.node;
29
+ if (childSpec.hidden)
30
+ continue;
31
+ commands.push(describeEntry(child, program));
32
+ }
33
+ return {
34
+ path: [...entry.path],
35
+ title: entry.title,
36
+ description: description ?? null,
37
+ arguments: entry.arguments.map(({ key, spec }) => ({
38
+ name: spec.valueName ?? key,
39
+ description: spec.description ?? null,
40
+ required: !spec.optional && !spec.hasDefault && (spec.variadic ? spec.min > 0 : true),
41
+ variadic: spec.variadic,
42
+ rest: spec.rest,
43
+ ...(spec.hasDefault ? { default: spec.default } : {}),
44
+ })),
45
+ flags,
46
+ examples: isGroup(node) ? [] : node.examples.map((example) => `${programName} ${example}`),
47
+ commands,
48
+ };
49
+ }
50
+ /** A declared flag as data. */
51
+ function describeFlag(spec) {
52
+ let boolean = spec.kind === "boolean";
53
+ return {
54
+ name: spec.name,
55
+ aliases: [...spec.aliases],
56
+ short: spec.short ?? null,
57
+ kind: spec.kind,
58
+ negatable: boolean,
59
+ value: boolean ? null : (spec.valueName ?? (spec.kind === "number" ? "number" : "value")),
60
+ optionalValue: spec.optionalValue,
61
+ list: spec.list,
62
+ repeatable: spec.multiple,
63
+ required: spec.required && spec.prompt === undefined,
64
+ description: spec.description ?? null,
65
+ builtin: false,
66
+ ...(spec.hasDefault && spec.default !== undefined ? { default: spec.default } : {}),
67
+ };
68
+ }
69
+ /**
70
+ * A flag the runtime answers for every command. `--no-progress` is the one spelled negated, so
71
+ * its name is `progress` and it is negatable.
72
+ */
73
+ function builtin(name, description, negatable) {
74
+ return {
75
+ name,
76
+ aliases: [],
77
+ short: null,
78
+ kind: "boolean",
79
+ negatable,
80
+ value: null,
81
+ optionalValue: false,
82
+ list: false,
83
+ repeatable: false,
84
+ required: false,
85
+ description,
86
+ builtin: true,
87
+ };
88
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The failures a command answers and the exit codes they map to: a usage failure prints the
3
+ * command's help and exits 2, a cancelled one exits 130 in silence, an exit failure picks its
4
+ * own code, and any other failure prints its message and exits 1.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import type { Failure, Success } from "@sdxc/result";
10
+ /** The exit code each outcome of a run maps to, shared by every command line built here. */
11
+ export declare const EXIT_CODES: {
12
+ readonly success: 0;
13
+ readonly failure: 1;
14
+ readonly usage: 2;
15
+ readonly cancelled: 130;
16
+ };
17
+ /** A command line the user wrote wrong; the run prints the message and the command's help. */
18
+ export declare class UsageError extends Error {
19
+ name: string;
20
+ }
21
+ /**
22
+ * The user stopped the run, with Ctrl+C or Escape at a prompt or Ctrl+C during a task. The run
23
+ * exits 130 and writes nothing more, since the user already knows why it stopped.
24
+ */
25
+ export declare class CancelledError extends Error {
26
+ name: string;
27
+ /** @param message - Read only by code that logs the failure; the run itself prints nothing */
28
+ constructor(message?: string);
29
+ }
30
+ /**
31
+ * A failure that picks its own exit code. An empty message prints nothing, for a command that
32
+ * already reported what went wrong, such as a test runner that printed its failed tests.
33
+ */
34
+ export declare class ExitError extends Error {
35
+ name: string;
36
+ /** The process exit code the run answers. */
37
+ code: number;
38
+ /**
39
+ * @param code - The exit code
40
+ * @param message - What the run prints on stderr, nothing when empty
41
+ */
42
+ constructor(code: number, message?: string);
43
+ }
44
+ /**
45
+ * A successful outcome; a command that has nothing to hand back calls it with no argument.
46
+ *
47
+ * @example return success();
48
+ */
49
+ export declare function success(): Success<undefined>;
50
+ export declare function success<T>(data: T): Success<T>;
51
+ /**
52
+ * A usage failure: the run prints `message`, then the help of the command that answered it, and
53
+ * exits 2.
54
+ *
55
+ * @example if (ctx.flags.prompt && !ctx.flags.from) return usage("--prompt needs --from");
56
+ */
57
+ export declare function usage(message: string): Failure<UsageError>;
58
+ /** A cancelled failure, which exits 130 without printing anything. */
59
+ export declare function cancelled(): Failure<CancelledError>;
60
+ /**
61
+ * A failure that exits with `code`, printing `message` when one is given.
62
+ *
63
+ * @example return run.failed > 0 ? exit(1) : success();
64
+ */
65
+ export declare function exit(code: number, message?: string): Failure<ExitError>;
66
+ /** The exit code a run's failure maps to. */
67
+ export declare function exitCodeOf(error: Error): number;
package/dist/errors.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The failures a command answers and the exit codes they map to: a usage failure prints the
3
+ * command's help and exits 2, a cancelled one exits 130 in silence, an exit failure picks its
4
+ * own code, and any other failure prints its message and exits 1.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import { failure, success as succeed } from "@sdxc/result";
10
+ /** The exit code each outcome of a run maps to, shared by every command line built here. */
11
+ export const EXIT_CODES = {
12
+ success: 0,
13
+ failure: 1,
14
+ usage: 2,
15
+ cancelled: 130,
16
+ };
17
+ /** A command line the user wrote wrong; the run prints the message and the command's help. */
18
+ export class UsageError extends Error {
19
+ name = "UsageError";
20
+ }
21
+ /**
22
+ * The user stopped the run, with Ctrl+C or Escape at a prompt or Ctrl+C during a task. The run
23
+ * exits 130 and writes nothing more, since the user already knows why it stopped.
24
+ */
25
+ export class CancelledError extends Error {
26
+ name = "CancelledError";
27
+ /** @param message - Read only by code that logs the failure; the run itself prints nothing */
28
+ constructor(message = "Cancelled") {
29
+ super(message);
30
+ }
31
+ }
32
+ /**
33
+ * A failure that picks its own exit code. An empty message prints nothing, for a command that
34
+ * already reported what went wrong, such as a test runner that printed its failed tests.
35
+ */
36
+ export class ExitError extends Error {
37
+ name = "ExitError";
38
+ /** The process exit code the run answers. */
39
+ code;
40
+ /**
41
+ * @param code - The exit code
42
+ * @param message - What the run prints on stderr, nothing when empty
43
+ */
44
+ constructor(code, message = "") {
45
+ super(message);
46
+ this.code = code;
47
+ }
48
+ }
49
+ export function success(data) {
50
+ return succeed(data);
51
+ }
52
+ /**
53
+ * A usage failure: the run prints `message`, then the help of the command that answered it, and
54
+ * exits 2.
55
+ *
56
+ * @example if (ctx.flags.prompt && !ctx.flags.from) return usage("--prompt needs --from");
57
+ */
58
+ export function usage(message) {
59
+ return failure(new UsageError(message));
60
+ }
61
+ /** A cancelled failure, which exits 130 without printing anything. */
62
+ export function cancelled() {
63
+ return failure(new CancelledError());
64
+ }
65
+ /**
66
+ * A failure that exits with `code`, printing `message` when one is given.
67
+ *
68
+ * @example return run.failed > 0 ? exit(1) : success();
69
+ */
70
+ export function exit(code, message) {
71
+ return failure(new ExitError(code, message));
72
+ }
73
+ /** The exit code a run's failure maps to. */
74
+ export function exitCodeOf(error) {
75
+ if (error instanceof UsageError)
76
+ return EXIT_CODES.usage;
77
+ if (error instanceof CancelledError)
78
+ return EXIT_CODES.cancelled;
79
+ if (error instanceof ExitError)
80
+ return error.code;
81
+ return EXIT_CODES.failure;
82
+ }
package/dist/exec.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Child processes without a shell: arguments reach the program exactly as given, so nothing in
3
+ * them is expanded or split. The answer is a `Result` carrying the exit code and what the child
4
+ * printed, and an aborted signal stops the child and answers a cancelled failure.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import type { Result } from "@sdxc/result";
10
+ import type { Output } from "./screen.js";
11
+ import type { Env } from "./style.js";
12
+ import { CancelledError } from "./errors.js";
13
+ /**
14
+ * Where a child's output goes: `"inherit"` to the CLI's own streams, `"ignore"` nowhere,
15
+ * `"collect"` into the answer only, and `"stderr"` sends the child's stdout to the CLI's stderr,
16
+ * so a command whose stdout is piped keeps it clean while an install prints.
17
+ */
18
+ export type Stdio = "inherit" | "ignore" | "collect" | "stderr";
19
+ /** How `ctx.exec` runs one command. */
20
+ export interface ExecOptions {
21
+ /** @default the command's working directory */
22
+ cwd?: string;
23
+ /** Variables set on top of the command's environment. */
24
+ env?: Env;
25
+ /** @default "inherit" */
26
+ stdio?: Stdio;
27
+ /** Stops the child. @default the run's signal, which aborts on Ctrl+C */
28
+ signal?: AbortSignal;
29
+ /** Text written to the child's stdin, which is otherwise closed (or the terminal for "inherit"). */
30
+ input?: string;
31
+ }
32
+ /** Everything an {@link Exec} receives, with every default filled in. */
33
+ export interface ExecRequest {
34
+ cwd: string;
35
+ env: Env;
36
+ stdio: Stdio;
37
+ signal: AbortSignal;
38
+ input: string | undefined;
39
+ /** The CLI's streams, for `"inherit"` and `"stderr"` output. */
40
+ stdout: Output;
41
+ stderr: Output;
42
+ }
43
+ /** A child that exited 0. */
44
+ export interface ExecOutput {
45
+ code: number;
46
+ /** What the child printed on stdout, empty unless it was collected. */
47
+ stdout: string;
48
+ stderr: string;
49
+ /** Both streams in the order they arrived. */
50
+ output: string;
51
+ }
52
+ /** A child that could not start or exited with a code other than 0. */
53
+ export declare class ExecError extends Error {
54
+ name: string;
55
+ /** The command as given. */
56
+ command: readonly string[];
57
+ /** The exit code, or `null` when the child never started or ended on a signal. */
58
+ code: number | null;
59
+ stdout: string;
60
+ stderr: string;
61
+ output: string;
62
+ /**
63
+ * @param message - What went wrong
64
+ * @param details - The command and what it printed
65
+ */
66
+ constructor(message: string, details: {
67
+ command: readonly string[];
68
+ code: number | null;
69
+ stdout?: string;
70
+ stderr?: string;
71
+ output?: string;
72
+ cause?: unknown;
73
+ });
74
+ }
75
+ /** Runs one command; `cli.run`'s `exec` option replaces it, as the test harness does. */
76
+ export type Exec = (command: readonly string[], request: ExecRequest) => Promise<Result<ExecOutput, ExecError | CancelledError>>;
77
+ /** The command as a shell user would read it, for messages. */
78
+ export declare function displayCommand(command: readonly string[]): string;
79
+ /**
80
+ * Spawns `command` with `node:child_process`. Output bound for the process's own streams is
81
+ * inherited directly; output bound for any other stream is piped and forwarded to it.
82
+ */
83
+ export declare const spawnExec: Exec;