hacksh 0.12.0 → 0.14.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/README.md +123 -92
- package/dist/command-CfWrbHLN.d.ts +378 -0
- package/dist/index.d.ts +17 -402
- package/dist/index.js +139 -1146
- package/dist/index.js.map +1 -1
- package/dist/pretty-BFub611x.js +1012 -0
- package/dist/pretty-BFub611x.js.map +1 -0
- package/dist/printer-api.d.ts +13 -0
- package/dist/printer-api.js +2 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Define a type-safe command-line interface with a Zod 4 object schema. The same schema parses and
|
|
4
4
|
validates arguments, infers the value passed to `run`, and generates help. Run external commands
|
|
5
|
-
with the context’s `exec`,
|
|
5
|
+
with the context’s `exec`, use its `logger` for progress, and request interactive input with `prompt`.
|
|
6
6
|
|
|
7
7
|
The name comes from 拍手 (_hakushu_), the Japanese word for "clap", as a nod to
|
|
8
8
|
[clap](https://github.com/clap-rs/clap), the command-line argument parser for Rust. It is spelled
|
|
@@ -35,12 +35,13 @@ const main = defineCommand({
|
|
|
35
35
|
version: "0.1.0",
|
|
36
36
|
description: "Show a commit summary",
|
|
37
37
|
args: z.object({
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
positionals: z.tuple([
|
|
39
|
+
z.string().default("HEAD").meta({ title: "REF", description: "Commit to inspect" }),
|
|
40
|
+
]),
|
|
41
|
+
repo: z.string().optional().meta({ description: "Repository directory", alias: "C" }),
|
|
40
42
|
}),
|
|
41
|
-
argOptions: { repo: { alias: "C" } },
|
|
42
43
|
async run({ args, exec }) {
|
|
43
|
-
const [ref] = args.
|
|
44
|
+
const [ref] = args.positionals;
|
|
44
45
|
const { stdout } = await exec(
|
|
45
46
|
"git",
|
|
46
47
|
["show", "--no-patch", "--format=%h %s", "--end-of-options", ref],
|
|
@@ -85,27 +86,27 @@ Boolean flags also accept the explicit forms `--verbose=true` and
|
|
|
85
86
|
|
|
86
87
|
## Positional arguments
|
|
87
88
|
|
|
88
|
-
Declare positional arguments as a `z.tuple` in the `
|
|
89
|
+
Declare positional arguments as a `z.tuple` in the `positionals` field. Tuple items are assigned in order,
|
|
89
90
|
and the tuple rest collects all remaining values. Optional or defaulted items must come after
|
|
90
|
-
required ones, and items cannot be booleans or arrays. The `
|
|
91
|
+
required ones, and items cannot be booleans or arrays. The `positionals` field is never a named option.
|
|
91
92
|
|
|
92
93
|
```ts
|
|
93
94
|
const copy = defineCommand({
|
|
94
95
|
name: "copy",
|
|
95
96
|
args: z.object({
|
|
96
|
-
|
|
97
|
-
[z.string().
|
|
98
|
-
z.string().
|
|
97
|
+
positionals: z.tuple(
|
|
98
|
+
[z.string().meta({ title: "DESTINATION", description: "Output directory" })],
|
|
99
|
+
z.string().meta({ title: "FILES", description: "Input files" }),
|
|
99
100
|
),
|
|
100
101
|
}),
|
|
101
102
|
run({ args }) {
|
|
102
|
-
const [destination, ...files] = args.
|
|
103
|
+
const [destination, ...files] = args.positionals; // [string, ...string[]]
|
|
103
104
|
},
|
|
104
105
|
});
|
|
105
106
|
```
|
|
106
107
|
|
|
107
108
|
```sh
|
|
108
|
-
copy out a.txt b.txt # {
|
|
109
|
+
copy out a.txt b.txt # { positionals: ["out", "a.txt", "b.txt"] }
|
|
109
110
|
```
|
|
110
111
|
|
|
111
112
|
Help and validation errors label each item with its `.meta({ title })`, such as `<DESTINATION>` and
|
|
@@ -117,17 +118,17 @@ additional positional arguments are rejected by default, as are unknown options.
|
|
|
117
118
|
|
|
118
119
|
A command rejects unknown options and excess positional arguments before its `run` handler or
|
|
119
120
|
schema transforms execute. For ordinary commands, declare the accepted arguments in `args`;
|
|
120
|
-
there is no need to inspect `
|
|
121
|
+
there is no need to inspect `input.unknown` manually. Errors are `HackshValidationError` instances,
|
|
121
122
|
with each rejected token in `issues[].arg`.
|
|
122
123
|
|
|
123
|
-
Everything after `--` is always collected unchanged in `passthrough`, independently of this policy.
|
|
124
|
+
Everything after `--` is always collected unchanged in `input.passthrough`, independently of this policy.
|
|
124
125
|
Use that explicit boundary when forwarding arguments to another executable:
|
|
125
126
|
|
|
126
127
|
```ts
|
|
127
128
|
const forward = defineCommand({
|
|
128
129
|
name: "git-forward",
|
|
129
|
-
async run({ exec,
|
|
130
|
-
const { stdout } = await exec("git", passthrough);
|
|
130
|
+
async run({ exec, input }) {
|
|
131
|
+
const { stdout } = await exec("git", input.passthrough);
|
|
131
132
|
console.log(stdout);
|
|
132
133
|
},
|
|
133
134
|
});
|
|
@@ -139,17 +140,17 @@ For a wrapper that also accepts undeclared arguments before `--`, set `allowUnkn
|
|
|
139
140
|
```ts
|
|
140
141
|
const wrapper = defineCommand({
|
|
141
142
|
allowUnknownArgs: true,
|
|
142
|
-
async run({
|
|
143
|
-
await exec("some-tool", [...
|
|
143
|
+
async run({ input, exec }) {
|
|
144
|
+
await exec("some-tool", [...input.unknown, ...input.passthrough]);
|
|
144
145
|
},
|
|
145
146
|
});
|
|
146
147
|
```
|
|
147
148
|
|
|
148
|
-
`
|
|
149
|
+
`input.unknown` preserves the order of unknown option tokens and excess positional values. Unknown
|
|
149
150
|
options do not consume a following value: if the schema has an unfilled positional slot, that
|
|
150
151
|
value fills it. Known options are still parsed and validated. Use `--` when a child's arguments
|
|
151
|
-
must not be interpreted by this command. A handler that does not use `passthrough` ignores those
|
|
152
|
-
arguments; commands that require rejecting them can check `passthrough.length` explicitly.
|
|
152
|
+
must not be interpreted by this command. A handler that does not use `input.passthrough` ignores those
|
|
153
|
+
arguments; commands that require rejecting them can check `input.passthrough.length` explicitly.
|
|
153
154
|
|
|
154
155
|
`allowUnknownArgs` defaults to `false`, inherits down the selected subcommand route, and can be
|
|
155
156
|
overridden by each child. A child's setting does not change validation of its parent's arguments.
|
|
@@ -161,37 +162,39 @@ including unknown arguments. Routing errors such as an unknown subcommand still
|
|
|
161
162
|
undeclared `--version` is an unknown argument, since the version builtin belongs to the root.
|
|
162
163
|
|
|
163
164
|
**Migrating from 0.11.0:** unknown input was previously accepted by default. Add
|
|
164
|
-
`allowUnknownArgs: true` to commands that intentionally use `
|
|
165
|
+
`allowUnknownArgs: true` to commands that intentionally use `input.unknown`, or to their shared parent.
|
|
165
166
|
No migration is needed for `--` forwarding.
|
|
166
167
|
|
|
167
|
-
## Argument
|
|
168
|
+
## Argument metadata
|
|
168
169
|
|
|
169
|
-
Use
|
|
170
|
-
schema keys, parsed output, or validation error paths. `z.enum` choices
|
|
171
|
-
appear in generated help, including
|
|
170
|
+
Use Zod 4 `.meta({ alias, valueName, env, title, description })` on each argument schema.
|
|
171
|
+
Metadata does not change schema keys, parsed output, or validation error paths. `z.enum` choices
|
|
172
|
+
and descriptions appear in generated help, including inside supported schema wrappers and tuple items.
|
|
172
173
|
|
|
173
174
|
```ts
|
|
174
175
|
const convert = defineCommand({
|
|
175
176
|
name: "convert",
|
|
176
177
|
args: z.object({
|
|
177
|
-
|
|
178
|
-
[z.enum(["small", "large"]).
|
|
179
|
-
z.string().
|
|
178
|
+
positionals: z.tuple(
|
|
179
|
+
[z.enum(["small", "large"]).meta({ title: "SIZE", description: "Item size" })],
|
|
180
|
+
z.string().meta({ title: "INPUT", description: "Input files" }),
|
|
180
181
|
),
|
|
181
|
-
format: z
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
format: z
|
|
183
|
+
.enum(["json", "yaml"])
|
|
184
|
+
.optional()
|
|
185
|
+
.meta({ description: "Output format", alias: "f", valueName: "FORMAT" }),
|
|
186
|
+
overwrite: z.boolean().default(true).meta({ description: "Replace existing files" }),
|
|
187
|
+
verbose: z
|
|
188
|
+
.boolean()
|
|
189
|
+
.default(false)
|
|
190
|
+
.meta({ description: "Show details", alias: ["v", "V"] }),
|
|
184
191
|
}),
|
|
185
|
-
argOptions: {
|
|
186
|
-
format: { alias: "f", valueName: "FORMAT" },
|
|
187
|
-
verbose: { alias: ["v", "V"] },
|
|
188
|
-
},
|
|
189
192
|
});
|
|
190
193
|
```
|
|
191
194
|
|
|
192
195
|
```sh
|
|
193
196
|
convert large -f yaml -v a.txt b.txt
|
|
194
|
-
# {
|
|
197
|
+
# { positionals: ["large", "a.txt", "b.txt"], format: "yaml", overwrite: true, verbose: true }
|
|
195
198
|
```
|
|
196
199
|
|
|
197
200
|
```text
|
|
@@ -211,18 +214,24 @@ OPTIONS
|
|
|
211
214
|
|
|
212
215
|
- `alias` gives a named option one or more single-character short forms.
|
|
213
216
|
- `valueName` sets the value label of a named option.
|
|
217
|
+
- `env` overrides an option's automatic environment variable name, or disables it with `false`.
|
|
218
|
+
- `title` labels a positional argument; `description` supplies its help text.
|
|
219
|
+
|
|
220
|
+
Place metadata on the outer schema to override metadata inherited through wrappers. Zod's native
|
|
221
|
+
`.describe(text)` remains equivalent to description metadata; new code should use `.meta({ description: text })`.
|
|
222
|
+
With `zod/mini`, use `.check(mini.meta({ ... }))`.
|
|
214
223
|
|
|
215
224
|
Boolean flags are shown as `--[no-]name` because they also accept the `--no-` form. The
|
|
216
225
|
`negatable` field of `UsageOptionSummary` carries the same information for machine-readable
|
|
217
226
|
help, where `flags` keeps the literal `--name` spelling.
|
|
218
227
|
|
|
219
|
-
`
|
|
220
|
-
|
|
221
|
-
|
|
228
|
+
`defineCommand` validates metadata types and throws a `TypeError` for empty or multi-character
|
|
229
|
+
aliases, duplicate aliases, and option-only metadata (`alias`, `env`, `valueName`) on positional
|
|
230
|
+
schemas. Without an alias of the same letter, `-h` and `-v` are shorthands
|
|
222
231
|
for `--help` and `--version`. Defining an `h` or `v` alias replaces the shorthand, so `convert -v`
|
|
223
|
-
above sets `verbose`; `--help` and `--version` remain available.
|
|
224
|
-
`TypeError` from `defineCommand
|
|
225
|
-
|
|
232
|
+
above sets `verbose`; `--help` and `--version` remain available. Both `help` and `version` option
|
|
233
|
+
names are reserved and throw a `TypeError` from `defineCommand` on every command.
|
|
234
|
+
Output selection does not reserve
|
|
226
235
|
any additional command-line flags, so names such as `--json`, `--output`, and `--log-format` remain
|
|
227
236
|
available to application schemas.
|
|
228
237
|
|
|
@@ -234,8 +243,11 @@ upper snake case.
|
|
|
234
243
|
|
|
235
244
|
```ts
|
|
236
245
|
const deploy = defineCommand({
|
|
237
|
-
args: z.object({
|
|
238
|
-
|
|
246
|
+
args: z.object({
|
|
247
|
+
target: z.string(),
|
|
248
|
+
dryRun: z.boolean().default(false),
|
|
249
|
+
token: z.string().meta({ env: "GITHUB_TOKEN" }),
|
|
250
|
+
}),
|
|
239
251
|
run: ({ args }) => args,
|
|
240
252
|
});
|
|
241
253
|
const main = defineCommand({ name: "my-cli", subCommands: { deploy } });
|
|
@@ -254,7 +266,7 @@ MY_CLI_DEPLOY_TARGET=prod MY_CLI_DEPLOY_DRY_RUN=true GITHUB_TOKEN=t my-cli deplo
|
|
|
254
266
|
positional arguments, and commands under an unnamed root get no derived name.
|
|
255
267
|
- Help shows the name as `(Env: MY_CLI_DEPLOY_TARGET)`, and `UsageOptionSummary.env` carries it.
|
|
256
268
|
Errors name the variable but never include its value.
|
|
257
|
-
- `runCommand
|
|
269
|
+
- `runCommand` and `runMain` read the `env` option, defaulting to `process.env`.
|
|
258
270
|
`parseArgs(command, argv, { env })` reads the environment only when `env` is given.
|
|
259
271
|
- Two options that resolve to the same name on one route, such as a root `deployTarget` and a
|
|
260
272
|
`deploy` subcommand's `target`, make the invocation throw a `TypeError`.
|
|
@@ -327,15 +339,15 @@ finishes. A printer failure rejects the invocation without changing a successful
|
|
|
327
339
|
|
|
328
340
|
## Interactive input
|
|
329
341
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
342
|
+
Every `run` context has a `prompt` with `select`, `confirm`, and `text` methods. Interactive input
|
|
343
|
+
is separate from `logger`: prompts share the invocation's selected printer, serialized output queue,
|
|
344
|
+
terminal lock, output mode, and abort signal.
|
|
333
345
|
|
|
334
346
|
```ts
|
|
335
347
|
const deploy = defineCommand({
|
|
336
348
|
name: "deploy",
|
|
337
|
-
async run({ logger }) {
|
|
338
|
-
const environment = await
|
|
349
|
+
async run({ logger, prompt }) {
|
|
350
|
+
const environment = await prompt.select({
|
|
339
351
|
message: "Environment",
|
|
340
352
|
options: [
|
|
341
353
|
{ value: "staging", hint: "safe default" },
|
|
@@ -343,9 +355,9 @@ const deploy = defineCommand({
|
|
|
343
355
|
] as const,
|
|
344
356
|
initialValue: "staging",
|
|
345
357
|
}); // "staging" | "production"
|
|
346
|
-
const confirmed = await
|
|
358
|
+
const confirmed = await prompt.confirm({ message: `Deploy to ${environment}?` });
|
|
347
359
|
if (!confirmed) return;
|
|
348
|
-
const release = await
|
|
360
|
+
const release = await prompt.text({
|
|
349
361
|
message: "Release name",
|
|
350
362
|
placeholder: "v1.2.3",
|
|
351
363
|
validate: (value) => (value.length === 0 ? "A release name is required" : undefined),
|
|
@@ -436,9 +448,9 @@ parse Git records or infer whether stdout is text, JSON, or a list.
|
|
|
436
448
|
### Execution options
|
|
437
449
|
|
|
438
450
|
Options are `cwd`, `env`, `input`, `reject`, `timeout` (milliseconds), `signal` (an
|
|
439
|
-
`AbortSignal`), `maxBuffer` (bytes per stream, default 100,000,000),
|
|
440
|
-
(milliseconds, default 5,000)
|
|
441
|
-
aborts. `input` is written to stdin and closed; otherwise stdin is empty.
|
|
451
|
+
`AbortSignal`), `maxBuffer` (bytes per stream, default 100,000,000), `forceKillAfterDelay`
|
|
452
|
+
(milliseconds, default 5,000), and `dryRun`. Every call also stops with `aborted` when the
|
|
453
|
+
invocation's `signal` aborts. `input` is written to stdin and closed; otherwise stdin is empty.
|
|
442
454
|
Output beyond `maxBuffer` is truncated to that many bytes. On timeout, abort, or output limit,
|
|
443
455
|
hacksh sends `SIGTERM` to the child it started and `SIGKILL` after `forceKillAfterDelay`; it does
|
|
444
456
|
not stop grandchildren, and it does not wait for them to close inherited pipes. Invalid arguments
|
|
@@ -458,6 +470,18 @@ JSON event has `phase: "start"`, `command` (`file`, `args`, and `cwd` when set),
|
|
|
458
470
|
in the event, the result's `command`, the `HackshExecError` message, and a `spawn` failure's
|
|
459
471
|
`error`. The child still receives the real values, and its captured output is returned unchanged.
|
|
460
472
|
|
|
473
|
+
`dryRun: true` does not start the child and does not write `input`. The result is
|
|
474
|
+
`{ status: "skipped", command }` with the redacted command and no `stdout`, `stderr`, or `exitCode`.
|
|
475
|
+
The `exec` event uses `phase: "skip"` and the same command, stdio, and `interactive` flag.
|
|
476
|
+
Pretty output marks that line with `#` instead of `$`. Omit `dryRun`, or pass `false`, to keep
|
|
477
|
+
today's success or failure result. A boolean `dryRun` returns the union of a skip and the executed
|
|
478
|
+
result, so narrow `status` before reading output. `reject` does not apply to a skip. An invocation
|
|
479
|
+
signal that is already aborted still fails as `aborted` before any skip event, and `reject` applies
|
|
480
|
+
to that failure. Option checks still run, including `input` together with `stdin: "inherit"`.
|
|
481
|
+
JSON mode, a non-interactive environment, and a busy terminal still show the plan and return
|
|
482
|
+
a skip. `display: false` hides the skip event. `dryRun` is not read from CLI flags or
|
|
483
|
+
`context.args`.
|
|
484
|
+
|
|
461
485
|
```ts
|
|
462
486
|
await exec("git", ["worktree", "add", path, branch], { cwd: repository });
|
|
463
487
|
await exec("gh", ["auth", "login", "--with-token"], { input: token, display: false });
|
|
@@ -562,7 +586,8 @@ to its caller, and returns the selected `run` handler's result. Pass a `printer`
|
|
|
562
586
|
receive all output produced by that invocation as structured events:
|
|
563
587
|
|
|
564
588
|
```ts
|
|
565
|
-
import { runCommand
|
|
589
|
+
import { runCommand } from "hacksh";
|
|
590
|
+
import type { PrintEvent, Printer } from "hacksh/printer";
|
|
566
591
|
|
|
567
592
|
const events: PrintEvent[] = [];
|
|
568
593
|
const printer: Printer = {
|
|
@@ -575,7 +600,7 @@ await runCommand(main, ["--help"], { printer });
|
|
|
575
600
|
// events[0] is { type: "usage", usage, text, stream: "stdout" }
|
|
576
601
|
```
|
|
577
602
|
|
|
578
|
-
The current events are `usage`, `version`, `error`, `log`, and `
|
|
603
|
+
The current events are `usage`, `version`, `error`, `log`, `prompt`, and `exec`. Usage events include the resolved
|
|
579
604
|
command's schema-free name, version, description, synopsis, arguments, options, and visible child
|
|
580
605
|
commands, plus rendered text and the intended `stdout` or `stderr` stream. Version events include
|
|
581
606
|
the version string. Error events contain a schema-free summary with an explicit `kind`, structured
|
|
@@ -585,68 +610,74 @@ the prompt kind, status, current value or choices, placeholder, and validation m
|
|
|
585
610
|
drained before `runCommand` or `runMain` completes or exits. A printer rejection rejects
|
|
586
611
|
`runCommand`.
|
|
587
612
|
|
|
613
|
+
The root `hacksh` entry exposes command-definition and execution APIs. Import printer factories,
|
|
614
|
+
output-mode helpers, `Printer`, `PrintEvent`, and renderer-facing event and summary types from
|
|
615
|
+
`hacksh/printer`. A custom printer can narrow `PrintEvent` on `event.type` without importing
|
|
616
|
+
each individual event type.
|
|
617
|
+
|
|
588
618
|
Use `createPrettyPrinter({ color, unicode, stdout, stderr })` to reuse the built-in human-readable
|
|
589
619
|
renderer with custom writable targets, or `createJsonPrinter({ stdout, stderr })` to reuse the
|
|
590
620
|
built-in JSON-lines behavior. The pretty renderer has no public theme API; replacing the fixed
|
|
591
|
-
presentation requires a custom `Printer`. `
|
|
592
|
-
undecorated compatibility adapters. `summarizeUsage(command)` returns the same canonical help
|
|
621
|
+
presentation requires a custom `Printer`. `summarizeUsage(command)` returns the same canonical help
|
|
593
622
|
metadata carried by a usage event. `resolveOutputMode(...)` exposes the selection contract for
|
|
594
|
-
wrappers and
|
|
623
|
+
wrappers and presentation or prompting layers.
|
|
595
624
|
|
|
596
|
-
For parsing and usage rendering without execution, use the
|
|
625
|
+
For parsing and usage rendering without execution, use the composition APIs. `parseArgs`
|
|
597
626
|
parses exactly the supplied command definition and argument segment, returns its schema output only,
|
|
598
|
-
and throws `HackshValidationError` on failure. It
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
takes precedence over printer selection; an explicit `printer` also retains its existing behavior.
|
|
605
|
-
These functions are async so future command-resolution features can be added without changing
|
|
606
|
-
their return types.
|
|
627
|
+
and throws `HackshValidationError` on failure. It remains asynchronous to support async Zod
|
|
628
|
+
refinements and transformations. It does not route subcommands, act on help or version tokens, or
|
|
629
|
+
bypass validation; callers that resolve commands themselves must pass the selected command and
|
|
630
|
+
its segment. `renderUsage` returns a string synchronously with no output side effects. `showUsage`
|
|
631
|
+
requires an explicit `printer`, sends the same text and structured help summary as a usage event,
|
|
632
|
+
and awaits the printer. Its optional `stream` defaults to `"stdout"`.
|
|
607
633
|
|
|
608
634
|
```ts
|
|
609
|
-
import {
|
|
635
|
+
import { parseArgs, renderUsage, showUsage } from "hacksh";
|
|
636
|
+
import { createPrettyPrinter } from "hacksh/printer";
|
|
610
637
|
|
|
611
638
|
const args = await parseArgs(main, ["42", "--repo", "owner/repo"]);
|
|
612
639
|
// args is inferred from the schema as:
|
|
613
|
-
// {
|
|
640
|
+
// { positionals: [string]; repo?: string }
|
|
614
641
|
|
|
615
|
-
const text: string =
|
|
642
|
+
const text: string = renderUsage(main); // does not write output
|
|
616
643
|
await showUsage(main, {
|
|
617
|
-
printer:
|
|
644
|
+
printer: createPrettyPrinter({ color: false, unicode: true }),
|
|
618
645
|
stream: "stderr",
|
|
619
646
|
});
|
|
620
647
|
```
|
|
621
648
|
|
|
622
|
-
The `run` context includes `
|
|
623
|
-
represented by a completed copy whose `name` is its key; the original definition is not mutated.
|
|
649
|
+
The `run` context includes `command`, the actual resolved command definition. An unnamed subcommand
|
|
650
|
+
is represented by a completed copy whose `name` is its key; the original definition is not mutated.
|
|
651
|
+
`args` contains validated schema output. `input` separates the original selected command segment
|
|
652
|
+
(`argv`), accepted unknown tokens (`unknown`), and tokens after `--` (`passthrough`).
|
|
653
|
+
For a routed child, `input.argv` excludes the parent options and subcommand names.
|
|
654
|
+
|
|
655
|
+
`defineCommand` preserves the handler's return type. `runCommand` returns a promise of the awaited
|
|
656
|
+
handler result, unioned with results from every possible routed descendant and `undefined` for
|
|
657
|
+
help/version. Routing-only commands add no handler result. Async and void handlers retain their
|
|
658
|
+
result types; `runMain` deliberately discards results.
|
|
624
659
|
|
|
625
660
|
`runMain(command)` reads `process.argv`, prints validation or usage errors, and exits with status 1.
|
|
626
|
-
Use `argv` to inject arguments
|
|
627
|
-
|
|
628
|
-
|
|
661
|
+
Use `argv` to inject arguments. Omitting it uses `process.argv.slice(2)`; an empty array is an
|
|
662
|
+
explicit empty invocation. `runMain` returns `Promise<void>`. A reusable entry point is an ordinary
|
|
663
|
+
local function:
|
|
629
664
|
|
|
630
665
|
```ts
|
|
631
|
-
import {
|
|
666
|
+
import { runMain } from "hacksh";
|
|
632
667
|
|
|
633
|
-
const mainEntry =
|
|
634
|
-
await mainEntry(); // uses the
|
|
635
|
-
await mainEntry([]); //
|
|
668
|
+
const mainEntry = (argv: string[] = ["--verbose"]) => runMain(main, { argv });
|
|
669
|
+
await mainEntry(); // uses the local default
|
|
670
|
+
await mainEntry([]); // explicitly supplies no arguments
|
|
636
671
|
```
|
|
637
672
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
output written by this application callback remains its own responsibility:
|
|
673
|
+
Help and the usage appended to validation or routing errors use the invocation's printer.
|
|
674
|
+
Usage events target stdout for help and stderr for errors. Pass `onError` to replace the entire
|
|
675
|
+
default error display, including usage; output written by this application callback remains its
|
|
676
|
+
own responsibility:
|
|
643
677
|
|
|
644
678
|
```ts
|
|
645
679
|
await runMain(main, {
|
|
646
680
|
argv: ["--help"],
|
|
647
|
-
async showUsage(command, { write }) {
|
|
648
|
-
await write(await renderUsage(command));
|
|
649
|
-
},
|
|
650
681
|
async onError(error) {
|
|
651
682
|
console.error(`gh-pr-commits: ${error instanceof Error ? error.message : String(error)}`);
|
|
652
683
|
},
|