hacksh 0.1.2 → 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/README.md CHANGED
@@ -35,9 +35,10 @@ const main = defineCommand({
35
35
  verbose: z.boolean().default(false).describe("Show details"),
36
36
  }),
37
37
  argOptions: { repo: { alias: "R" }, verbose: { alias: ["v", "V"] } },
38
- async run({ args, rawArgs, extraArgs, passthrough }) {
38
+ async run({ args, rawArgs, extraArgs, passthrough, logger }) {
39
39
  // args is { _: [number?]; repo?: string; verbose: boolean }
40
- console.log({ args, rawArgs, extraArgs, passthrough });
40
+ logger.info("Loading commits for {repo}", { repo: args.repo ?? "the current repository" });
41
+ logger.debug("Invocation arguments", { rawArgs, extraArgs, passthrough });
41
42
  },
42
43
  });
43
44
 
@@ -143,8 +144,9 @@ OPTIONS
143
144
  when an alias is longer than one character or is used by more than one option, and when
144
145
  `argOptions` has a `_` key. Without an alias of the same letter, `-h` and `-v` are shorthands
145
146
  for `--help` and `--version`. Defining an `h` or `v` alias replaces the shorthand, so `convert -v`
146
- above sets `verbose`; `--help` and `--version` remain available. Help remains plain text when
147
- `NO_COLOR` or `CI` is set.
147
+ above sets `verbose`; `--help` and `--version` remain available. Output selection does not reserve
148
+ any additional command-line flags, so names such as `--json`, `--output`, and `--log-format` remain
149
+ available to application schemas.
148
150
 
149
151
  ## Subcommands
150
152
 
@@ -155,8 +157,8 @@ const main = defineCommand({
155
157
  subCommands: {
156
158
  new: defineCommand({
157
159
  args: z.object({ name: z.string() }),
158
- async run({ args }) {
159
- console.log(args.name);
160
+ async run({ args, logger }) {
161
+ logger.success("Created {name}", { name: args.name });
160
162
  },
161
163
  }),
162
164
  migrate: defineCommand({
@@ -179,22 +181,209 @@ listing, including usage shown for an unknown command. Hidden commands remain av
179
181
  their own `--help` output remains available. If every subcommand is hidden, help keeps the generic
180
182
  `<COMMAND>` syntax but omits the empty `COMMANDS` section.
181
183
 
184
+ ## Structured logging
185
+
186
+ Every `run` context has a `logger`. Its `trace`, `debug`, `info`, `warn`, `error`, and `fatal`
187
+ methods emit structured records through the invocation's printer. `step` and `success` are
188
+ info-level records with a distinct semantic kind for presentation. Placeholders in braces use the
189
+ record's properties. Every level is emitted and displayed by the default printer; there is no
190
+ built-in severity threshold or verbose flag.
191
+
192
+ ```ts
193
+ const deploy = defineCommand({
194
+ name: "deploy",
195
+ async run({ logger }) {
196
+ const log = logger.with({ app: "web", env: "prod" });
197
+ log.step("Deploying {app} to {env}");
198
+ log.getChild("upload").debug("Uploaded {files} files", { files: 12 });
199
+ log.success("Deployed {app}");
200
+ },
201
+ });
202
+ ```
203
+
204
+ Properties passed to an individual call override properties bound by `with`. `getChild` appends a
205
+ category segment; hacksh automatically starts the category with the named root and each resolved
206
+ subcommand. Missing placeholders, and placeholders whose values cannot be converted to text,
207
+ remain unchanged without discarding their properties. Passing an `Error` to `error` or `fatal`
208
+ uses its message and adds a canonical, schema-free error summary as `properties.error`. Error
209
+ values nested in other log properties are summarized the same way before custom printers receive
210
+ the event.
211
+
212
+ Each `log` print event contains a `record` with `level`, `kind`, `category`, interpolated `message`,
213
+ template `rawMessage`, merged `properties`, and a numeric `timestamp`. Logger calls do not need to
214
+ be awaited: they use the invocation's serialized output queue, which is drained before execution
215
+ finishes. A printer failure rejects the invocation without changing a successful `run` value.
216
+
217
+ ## Interactive input
218
+
219
+ The logger also owns the invocation's interactive input. Use `select`, `confirm`, and `text` from
220
+ the same logger (including loggers returned by `with` and `getChild`) so prompts use the selected
221
+ printer and output mode.
222
+
223
+ ```ts
224
+ const deploy = defineCommand({
225
+ name: "deploy",
226
+ async run({ logger }) {
227
+ const environment = await logger.select({
228
+ message: "Environment",
229
+ options: [
230
+ { value: "staging", hint: "safe default" },
231
+ { value: "production", label: "Production" },
232
+ ] as const,
233
+ initialValue: "staging",
234
+ }); // "staging" | "production"
235
+ const confirmed = await logger.confirm({ message: `Deploy to ${environment}?` });
236
+ if (!confirmed) return;
237
+ const release = await logger.text({
238
+ message: "Release name",
239
+ placeholder: "v1.2.3",
240
+ validate: (value) => (value.length === 0 ? "A release name is required" : undefined),
241
+ });
242
+ logger.success("Deploying {release}", { release });
243
+ },
244
+ });
245
+ ```
246
+
247
+ Select values retain their literal union type. Arrow keys change the selected choice, Enter accepts
248
+ it, and `initialValue` controls the initial selection. Confirm accepts `y`, `n`, or Enter; Enter
249
+ uses `initialValue` when supplied and otherwise means no. Text displays its placeholder without
250
+ using it as input, supports editing, and repeats the prompt only when `validate` returns a nonempty
251
+ message; `""` means valid. Backspace removes one Unicode code point. Input pasted after a selection
252
+ or confirmation is retained for the next sequential prompt instead of being discarded.
253
+ The built-in prompt API intentionally stops at select, confirm, and text; it does not provide a
254
+ multiselect prompt.
255
+
256
+ Prompts are allowed only when the resolved mode is interactive: pretty output outside CI with TTY
257
+ stdin and stdout. JSON, CI, and non-TTY invocations throw `HackshPromptError` immediately even if a
258
+ default is available. Its `reason` and `prompt` fields identify why input failed and the prompt's
259
+ `kind`, `message`, and select `choices`. Reasons distinguish `non-interactive`, `cancelled`, `eof`,
260
+ `overlapping`, `invalid-options`, `input-error`, and `printer-error`. Ctrl-C and Escape cancel;
261
+ `runMain` reports cancellation and exits with status 130. Only one prompt may be active in an
262
+ invocation. Input listeners and raw terminal mode are restored after every outcome.
263
+ Failed prompts emit a non-visible `closed` prompt state through the same printer before rejecting,
264
+ so callers that catch cancellation, EOF, input, or printer errors do not leave stale terminal UI.
265
+
266
+ Active pretty prompts redraw in place rather than appending a block for every key. A completed
267
+ prompt collapses to one result line. If a logger event arrives while a prompt is active, the
268
+ printer removes the prompt, renders the event, and then redraws the current prompt state.
269
+ Physical rows are calculated from explicit newlines, ANSI-free Unicode display width, and
270
+ `stdout.columns`, including terminal wrapping. When a custom output writer does not expose a
271
+ positive `columns` value, the renderer conservatively counts explicit lines only.
272
+
273
+ ## Output formats
274
+
275
+ `runMain` and `runCommand` resolve one output mode before command routing, parsing, or execution.
276
+ Their `output` option accepts `"auto"` (the default), `"pretty"`, or `"json"`. A valid
277
+ `HACKSH_OUTPUT` environment value takes precedence over that option. `auto` selects JSON only when
278
+ `CI` has a non-empty value; redirecting or piping stdout alone retains pretty output. Invalid
279
+ explicit option or environment values never execute the command. `runCommand` rejects with a
280
+ `TypeError`. `runMain` gives that error to `onError` when configured; otherwise it emits the error
281
+ through a supplied printer or as a dedicated JSON error line, drains output, and exits with status
282
+ code `1`. Output values are case-sensitive.
283
+
284
+ ```ts
285
+ await runMain(main, { output: "auto" });
286
+ // HACKSH_OUTPUT=json my-cli --help overrides the configured preference.
287
+ ```
288
+
289
+ The resolved `OutputMode` has `format`, `interactive`, and `color` fields. Interaction is enabled
290
+ only for pretty output outside CI when both stdin and stdout are TTYs. Color is enabled only for
291
+ pretty output to a stdout TTY when `NO_COLOR` is empty or absent. These decisions are independent: an
292
+ explicit pretty format in CI can use color, but remains noninteractive. `env`, `stdin`, `stdout`,
293
+ and `stderr` can be supplied in execution options for embedded or tested invocations.
294
+ Custom `stdin` values implement the readable-stream operations `on`, `removeListener`, `resume`,
295
+ and `pause`; `isTTY`, `isRaw`, `readableFlowing`, and `setRawMode` describe optional terminal
296
+ capabilities. A stream without those readable operations fails with an `input-error` if prompting
297
+ is attempted.
298
+
299
+ JSON output contains exactly one `PrintEvent` object per line. Help and version events and
300
+ trace/debug/info logs go to stdout; errors and warning/error/fatal logs go to stderr. Usage attached
301
+ to an error retains its `stderr` stream. Every log level is emitted by default. JSON strings have
302
+ terminal control sequences removed, and the printer never serializes executable command or Zod
303
+ schema objects.
304
+
305
+ Arbitrary log properties and error details are normalized predictably: BigInt values become
306
+ decimal strings; non-finite numbers become `null`; `undefined`, symbols, and functions receive
307
+ explicit string markers; dates and URLs use their guarded `toJSON` representations; Buffer uses
308
+ its `{ type, data }` representation; typed arrays become arrays; maps and sets become arrays; and
309
+ cycles become `"[Circular]"`. ANSI sequences are removed from keys as well as values. Errors retain
310
+ their kind, name, message, stack, canonical cause, validation issues, and independently readable
311
+ enumerable properties. Unreadable properties become `"[Unserializable]"`; a throwing `toJSON`
312
+ falls back to those properties. If an exotic value prevents normalization entirely, the line
313
+ keeps the original event `type` and adds `serializationError`, so downstream JSON-lines parsing
314
+ remains synchronized.
315
+
316
+ Pretty output uses a fixed presentation with semantic marks and a continuation bar. Trace, debug,
317
+ info, warning, error, and fatal records each have a distinct mark; step and success records have
318
+ their own marks as well, so meaning does not depend on color. Categories appear before log messages,
319
+ and every line of multiline messages, properties, help, and errors remains inside the same visual
320
+ block. All six log levels are displayed by default.
321
+
322
+ ```text
323
+ ◇ [deploy] Building web
324
+ │ files: 12
325
+ ✔ [deploy] Deployed web
326
+ ▲ [deploy] Cache was not cleared
327
+ ```
328
+
329
+ Pretty output uses Unicode symbols by default. Set `unicode: false` on `runMain` or `runCommand` for
330
+ ASCII marks and bars. Color is still resolved from the selected output mode: it requires pretty
331
+ output and a stdout TTY, and any non-empty `NO_COLOR` disables it. Unicode selection does not affect
332
+ JSON output. An invocation does not add automatic start or end messages; the renderer presents only
333
+ the semantic events that the command emits.
334
+
182
335
  ## Programmatic execution and errors
183
336
 
184
- `runCommand(command, argv)` handles help and version output without exiting, throws errors to its
185
- caller, and returns the selected `run` handler's result.
337
+ `runCommand(command, argv, options)` handles help and version output without exiting, throws errors
338
+ to its caller, and returns the selected `run` handler's result. Pass a `printer` in `options` to
339
+ receive all output produced by that invocation as structured events:
340
+
341
+ ```ts
342
+ import { runCommand, type PrintEvent, type Printer } from "hacksh";
343
+
344
+ const events: PrintEvent[] = [];
345
+ const printer: Printer = {
346
+ async print(event) {
347
+ events.push(event);
348
+ },
349
+ };
350
+
351
+ await runCommand(main, ["--help"], { printer });
352
+ // events[0] is { type: "usage", usage, text, stream: "stdout" }
353
+ ```
354
+
355
+ The current events are `usage`, `version`, `error`, `log`, and `prompt`. Usage events include the resolved
356
+ command's schema-free name, version, description, synopsis, arguments, options, and visible child
357
+ commands, plus rendered text and the intended `stdout` or `stderr` stream. Version events include
358
+ the version string. Error events contain a schema-free summary with an explicit `kind`, structured
359
+ validation issues, standard `Error` fields, custom enumerable properties, and recursively
360
+ canonical causes; log events contain the structured record described above. Prompt events contain
361
+ the prompt kind, status, current value or choices, placeholder, and validation message. Printer calls are serialized, may be asynchronous, and are
362
+ drained before `runCommand` or `runMain` completes or exits. A printer rejection rejects
363
+ `runCommand`.
364
+
365
+ Use `createPrettyPrinter({ color, unicode, stdout, stderr })` to reuse the built-in human-readable
366
+ renderer with custom writable targets, or `createJsonPrinter({ stdout, stderr })` to reuse the
367
+ built-in JSON-lines behavior. The pretty renderer has no public theme API; replacing the fixed
368
+ presentation requires a custom `Printer`. `consolePrinter` and `createStreamPrinter` remain the
369
+ undecorated compatibility adapters. `summarizeUsage(command)` returns the same canonical help
370
+ metadata carried by a usage event. `resolveOutputMode(...)` exposes the selection contract for
371
+ wrappers and the later presentation or prompting layers.
186
372
 
187
373
  For parsing and usage rendering without execution, use the async composition APIs. `parseArgs`
188
374
  parses exactly the supplied command definition and argument segment, returns its schema output only,
189
375
  and throws `ZittyValidationError` on failure. It does not route subcommands, act on help or version
190
376
  tokens, or bypass validation; callers that resolve commands themselves must pass the selected
191
377
  command and its segment. `renderUsage` has no output side effects; `showUsage` sends that same text
192
- to an optional synchronous or asynchronous writer (and otherwise uses `console.log`). These
193
- functions are async so future command-resolution features can be added without changing their
194
- return types.
378
+ through an explicit printer or the same automatic pretty/JSON selection used by command
379
+ invocations. Its `output`, `env`, `stdin`, `stdout`, `stderr`, and `unicode` options configure that
380
+ automatic path. The existing synchronous or asynchronous `write` option remains supported and
381
+ takes precedence over printer selection; an explicit `printer` also retains its existing behavior.
382
+ These functions are async so future command-resolution features can be added without changing
383
+ their return types.
195
384
 
196
385
  ```ts
197
- import { parseArgs, renderUsage, showUsage } from "hacksh";
386
+ import { consolePrinter, parseArgs, renderUsage, showUsage } from "hacksh";
198
387
 
199
388
  const args = await parseArgs(main, ["42", "--repo", "owner/repo"]);
200
389
  // args is inferred from the schema as:
@@ -202,10 +391,8 @@ const args = await parseArgs(main, ["42", "--repo", "owner/repo"]);
202
391
 
203
392
  const text: string = await renderUsage(main); // does not write output
204
393
  await showUsage(main, {
205
- async write(usage) {
206
- await Promise.resolve();
207
- console.info(usage);
208
- },
394
+ printer: consolePrinter,
395
+ stream: "stderr",
209
396
  });
210
397
  ```
211
398
 
@@ -225,9 +412,11 @@ await mainEntry(); // uses the configured argv
225
412
  await mainEntry([]); // an explicit empty invocation overrides it
226
413
  ```
227
414
 
228
- `showUsage` customizes both ordinary help and the usage appended to validation or routing errors.
229
- The supplied `write` function targets stdout for help and stderr for errors. The callback may be
230
- async. Pass `onError` to replace the entire default error display, including usage:
415
+ The legacy `RunMainOptions.showUsage` callback still customizes both ordinary help and the usage
416
+ appended to validation or routing errors. Its supplied `write` function targets stdout for help and
417
+ stderr for errors by emitting usage events through the invocation's selected printer, and the
418
+ callback may be async. Pass `onError` to replace the entire default error display, including usage;
419
+ output written by this application callback remains its own responsibility:
231
420
 
232
421
  ```ts
233
422
  await runMain(main, {
@@ -243,16 +432,18 @@ await runMain(main, {
243
432
 
244
433
  Usage failures are `ZittyUsageError` instances. Argument failures are
245
434
  `ZittyValidationError` instances whose `issues` identify CLI names such as `--repo` or `<PR>`.
435
+ Prompt failures are `HackshPromptError` instances with structured `reason` and `prompt` fields.
246
436
 
247
437
  ## Development
248
438
 
249
- | Command | Description |
250
- | ------------------ | ----------------------------------------- |
251
- | `vp check` | Format, lint, and type check |
252
- | `vp test` | Run tests |
253
- | `vp pack` | Build `src/index.ts` into `dist` |
254
- | `vp run ready` | Run check, test, and build |
255
- | `pnpm test:packed` | Test the tarball with Node 22.18 and Deno |
439
+ | Command | Description |
440
+ | ---------------------- | ----------------------------------------------------- |
441
+ | `vp check` | Format, lint, and type check |
442
+ | `vp test` | Run tests |
443
+ | `pnpm test:prompt-pty` | Probe prompts in a real terminal |
444
+ | `vp pack` | Build `src/index.ts` into `dist` |
445
+ | `vp run ready` | Run check, test, and build |
446
+ | `pnpm test:packed` | Test the tarball with Node 22.18, host Node, and Deno |
256
447
 
257
448
  The packed-consumer test must execute its Node consumer with exactly Node 22.18.0. Run pnpm itself
258
449
  under that version, or supply an absolute executable path explicitly:
package/dist/index.d.ts CHANGED
@@ -1,4 +1,270 @@
1
1
  import { $ZodIssue, $ZodObject, output } from "zod/v4/core";
2
+ //#region src/errors.d.ts
3
+ interface ZittyIssue {
4
+ arg: string;
5
+ message: string;
6
+ issue?: $ZodIssue;
7
+ }
8
+ declare class ZittyUsageError extends Error {
9
+ readonly command: AnyCommandDef;
10
+ constructor(message: string, command: AnyCommandDef, options?: ErrorOptions);
11
+ }
12
+ declare class ZittyValidationError extends ZittyUsageError {
13
+ readonly issues: ReadonlyArray<ZittyIssue>;
14
+ constructor(command: AnyCommandDef, issues: ReadonlyArray<ZittyIssue>);
15
+ }
16
+ type PromptKind = "select" | "confirm" | "text";
17
+ type HackshPromptErrorReason = "non-interactive" | "cancelled" | "eof" | "overlapping" | "invalid-options" | "input-error" | "printer-error";
18
+ interface PromptErrorDetails {
19
+ kind: PromptKind;
20
+ message: string;
21
+ choices?: readonly string[];
22
+ }
23
+ /** An input request that could not be completed safely. */
24
+ declare class HackshPromptError extends Error {
25
+ readonly reason: HackshPromptErrorReason;
26
+ readonly prompt: PromptErrorDetails;
27
+ constructor(reason: HackshPromptErrorReason, prompt: PromptErrorDetails, message: string, options?: ErrorOptions);
28
+ }
29
+ type HackshErrorKind = "error" | "prompt" | "usage" | "validation" | "thrown";
30
+ interface ValidationIssueSummary {
31
+ arg: string;
32
+ message: string;
33
+ code?: string;
34
+ path?: readonly PropertyKey[];
35
+ details?: Readonly<Record<string, unknown>>;
36
+ }
37
+ interface HackshErrorSummary {
38
+ kind: HackshErrorKind;
39
+ name: string;
40
+ message: string;
41
+ stack?: string;
42
+ cause?: unknown;
43
+ issues?: readonly ValidationIssueSummary[];
44
+ properties?: Readonly<Record<string, unknown>>;
45
+ value?: unknown;
46
+ }
47
+ //#endregion
48
+ //#region src/output.d.ts
49
+ interface OutputOptions {
50
+ output?: OutputPreference;
51
+ /** Use Unicode presentation symbols in pretty output. Defaults to true. */
52
+ unicode?: boolean;
53
+ env?: Readonly<Record<string, string | undefined>>;
54
+ stdin?: TerminalInput;
55
+ stdout?: TerminalOutput;
56
+ stderr?: OutputWriter;
57
+ }
58
+ //#endregion
59
+ //#region src/usage.d.ts
60
+ interface UsageArgumentSummary {
61
+ name: string;
62
+ required: boolean;
63
+ variadic: boolean;
64
+ description?: string;
65
+ choices?: readonly string[];
66
+ defaultValue?: unknown;
67
+ }
68
+ interface UsageOptionSummary {
69
+ name: string;
70
+ flags: readonly string[];
71
+ takesValue: boolean;
72
+ valueName?: string;
73
+ required: boolean;
74
+ description?: string;
75
+ choices?: readonly string[];
76
+ defaultValue?: unknown;
77
+ negatedDescription?: string;
78
+ }
79
+ interface UsageCommandSummary {
80
+ name: string;
81
+ description?: string;
82
+ }
83
+ /** A schema-free description of the useful help metadata for a command. */
84
+ interface UsageSummary {
85
+ name: string;
86
+ heading?: string;
87
+ version?: string;
88
+ description?: string;
89
+ synopsis: string;
90
+ arguments: readonly UsageArgumentSummary[];
91
+ options: readonly UsageOptionSummary[];
92
+ commands: readonly UsageCommandSummary[];
93
+ }
94
+ /** Build a serializable help summary without retaining command or schema objects. */
95
+ declare function summarizeUsage(command: AnyCommandDef): UsageSummary;
96
+ type UsageWriter = (text: string) => void | Promise<void>;
97
+ interface ShowUsageOptions extends OutputOptions {
98
+ write?: UsageWriter;
99
+ printer?: Printer;
100
+ stream?: PrintStream;
101
+ }
102
+ type UsageOutput = Required<Pick<ShowUsageOptions, "write">>;
103
+ type ShowUsageCallback = (command: AnyCommandDef, output: UsageOutput) => void | Promise<void>;
104
+ /** Generate usage text without writing to stdout or stderr. */
105
+ declare function renderUsage(command: AnyCommandDef): Promise<string>;
106
+ /** Generate and write usage text, awaiting asynchronous writers. */
107
+ declare function showUsage(command: AnyCommandDef, options?: ShowUsageOptions): Promise<void>;
108
+ //#endregion
109
+ //#region src/printer.d.ts
110
+ type PrintStream = "stdout" | "stderr";
111
+ type OutputPreference = "auto" | "pretty" | "json";
112
+ type OutputFormat = Exclude<OutputPreference, "auto">;
113
+ interface OutputMode {
114
+ format: OutputFormat;
115
+ interactive: boolean;
116
+ color: boolean;
117
+ }
118
+ interface ResolveOutputModeOptions {
119
+ output?: OutputPreference;
120
+ env?: Readonly<Record<string, string | undefined>>;
121
+ inputIsTTY?: boolean;
122
+ outputIsTTY?: boolean;
123
+ }
124
+ /** Resolve output behavior once, before command routing or argument parsing. */
125
+ declare function resolveOutputMode(options?: ResolveOutputModeOptions): OutputMode;
126
+ interface UsagePrintEvent {
127
+ type: "usage";
128
+ usage: UsageSummary;
129
+ text: string;
130
+ stream: PrintStream;
131
+ }
132
+ interface VersionPrintEvent {
133
+ type: "version";
134
+ version: string;
135
+ }
136
+ interface ErrorPrintEvent {
137
+ type: "error";
138
+ error: HackshErrorSummary;
139
+ }
140
+ interface LogPrintEvent {
141
+ type: "log";
142
+ record: LogRecord;
143
+ }
144
+ interface PromptChoiceState {
145
+ value: string;
146
+ label: string;
147
+ hint?: string;
148
+ selected: boolean;
149
+ }
150
+ type PromptStatus = "active" | "error" | "submitted" | "closed";
151
+ interface PromptStateBase {
152
+ message: string;
153
+ status: PromptStatus;
154
+ error?: string;
155
+ }
156
+ interface SelectPromptState extends PromptStateBase {
157
+ kind: "select";
158
+ choices: readonly PromptChoiceState[];
159
+ value: string;
160
+ }
161
+ interface ConfirmPromptState extends PromptStateBase {
162
+ kind: "confirm";
163
+ value: boolean;
164
+ defaultValue?: boolean;
165
+ }
166
+ interface TextPromptState extends PromptStateBase {
167
+ kind: "text";
168
+ value: string;
169
+ placeholder?: string;
170
+ }
171
+ type PromptState = SelectPromptState | ConfirmPromptState | TextPromptState;
172
+ interface PromptPrintEvent {
173
+ type: "prompt";
174
+ prompt: PromptState;
175
+ }
176
+ /** A semantic, command- and schema-free output value emitted by hacksh. */
177
+ type PrintEvent = UsagePrintEvent | VersionPrintEvent | ErrorPrintEvent | LogPrintEvent | PromptPrintEvent;
178
+ /** Receives every output event produced by one command invocation. */
179
+ interface Printer {
180
+ print(event: PrintEvent): void | Promise<void>;
181
+ }
182
+ /** Undecorated console compatibility adapter; automatic output uses the selected format printer. */
183
+ declare const consolePrinter: Printer;
184
+ interface OutputWriter {
185
+ write(text: string): unknown;
186
+ }
187
+ interface TerminalInput {
188
+ readonly isTTY?: boolean;
189
+ readonly isRaw?: boolean;
190
+ readonly readableFlowing?: boolean | null;
191
+ setRawMode?(mode: boolean): unknown;
192
+ resume(): unknown;
193
+ pause(): unknown;
194
+ on(event: string, listener: (...args: never[]) => void): unknown;
195
+ removeListener(event: string, listener: (...args: never[]) => void): unknown;
196
+ }
197
+ interface TerminalOutput extends OutputWriter {
198
+ readonly isTTY?: boolean;
199
+ readonly columns?: number;
200
+ }
201
+ interface JsonPrinterOptions {
202
+ stdout?: OutputWriter;
203
+ stderr?: OutputWriter;
204
+ }
205
+ interface StreamPrinterOptions {
206
+ stdout: OutputWriter;
207
+ stderr: OutputWriter;
208
+ }
209
+ /** Create an undecorated stream-backed compatibility adapter. */
210
+ declare function createStreamPrinter(options: StreamPrinterOptions): Printer;
211
+ /** Create a one-semantic-event-per-line JSON printer. */
212
+ declare function createJsonPrinter(options?: JsonPrinterOptions): Printer;
213
+ //#endregion
214
+ //#region src/prompts.d.ts
215
+ interface SelectOption<T extends string> {
216
+ value: T;
217
+ label?: string;
218
+ hint?: string;
219
+ }
220
+ interface SelectOptions<T extends string> {
221
+ message: string;
222
+ options: readonly SelectOption<T>[];
223
+ initialValue?: T;
224
+ }
225
+ interface ConfirmOptions {
226
+ message: string;
227
+ initialValue?: boolean;
228
+ }
229
+ interface TextOptions {
230
+ message: string;
231
+ placeholder?: string;
232
+ validate?(value: string): string | undefined;
233
+ }
234
+ interface Prompter {
235
+ select<const T extends string>(options: SelectOptions<T>): Promise<T>;
236
+ confirm(options: ConfirmOptions): Promise<boolean>;
237
+ text(options: TextOptions): Promise<string>;
238
+ }
239
+ //#endregion
240
+ //#region src/logger.d.ts
241
+ type LogLevel = "trace" | "debug" | "info" | "warning" | "error" | "fatal";
242
+ type LogKind = "message" | "step" | "success";
243
+ type LogProperties = Record<string, unknown>;
244
+ /** A structured message emitted by a command's logger. */
245
+ interface LogRecord {
246
+ level: LogLevel;
247
+ kind: LogKind;
248
+ category: readonly string[];
249
+ message: string;
250
+ rawMessage: string;
251
+ properties: LogProperties;
252
+ timestamp: number;
253
+ }
254
+ interface Logger extends Prompter {
255
+ readonly category: readonly string[];
256
+ trace(message: string, properties?: LogProperties): void;
257
+ debug(message: string, properties?: LogProperties): void;
258
+ info(message: string, properties?: LogProperties): void;
259
+ warn(message: string, properties?: LogProperties): void;
260
+ error(message: string | Error, properties?: LogProperties): void;
261
+ fatal(message: string | Error, properties?: LogProperties): void;
262
+ step(message: string, properties?: LogProperties): void;
263
+ success(message: string, properties?: LogProperties): void;
264
+ with(properties: LogProperties): Logger;
265
+ getChild(category: string): Logger;
266
+ }
267
+ //#endregion
2
268
  //#region src/schema.d.ts
3
269
  type ArgsSchema = $ZodObject;
4
270
  type EmptyArgs = $ZodObject<{}>;
@@ -20,6 +286,7 @@ interface CommandContext<S extends ArgsSchema> {
20
286
  rawArgs: string[];
21
287
  passthrough: string[];
22
288
  extraArgs: string[];
289
+ logger: Logger;
23
290
  }
24
291
  interface CommandDef<S extends ArgsSchema = EmptyArgs> {
25
292
  name?: string;
@@ -34,48 +301,36 @@ interface CommandDef<S extends ArgsSchema = EmptyArgs> {
34
301
  type AnyCommandDef = CommandDef<ArgsSchema>;
35
302
  declare function defineCommand<S extends ArgsSchema = EmptyArgs>(def: CommandDef<S>): CommandDef<S>;
36
303
  //#endregion
37
- //#region src/errors.d.ts
38
- interface ZittyIssue {
39
- arg: string;
40
- message: string;
41
- issue?: $ZodIssue;
42
- }
43
- declare class ZittyUsageError extends Error {
44
- readonly command: AnyCommandDef;
45
- constructor(message: string, command: AnyCommandDef, options?: ErrorOptions);
46
- }
47
- declare class ZittyValidationError extends ZittyUsageError {
48
- readonly issues: ReadonlyArray<ZittyIssue>;
49
- constructor(command: AnyCommandDef, issues: ReadonlyArray<ZittyIssue>);
50
- }
51
- //#endregion
52
304
  //#region src/parser.d.ts
53
305
  /**
54
306
  * Parse and validate one command segment without routing, builtin handling, or output.
55
307
  */
56
308
  declare function parseArgs<S extends ArgsSchema>(command: CommandDef<S>, argv: string[]): Promise<output<S>>;
57
309
  //#endregion
58
- //#region src/usage.d.ts
59
- type UsageWriter = (text: string) => void | Promise<void>;
60
- interface ShowUsageOptions {
61
- write?: UsageWriter;
310
+ //#region src/pretty.d.ts
311
+ interface PrettyPrinterOptions {
312
+ color: boolean;
313
+ unicode: boolean;
314
+ stdout?: TerminalOutput;
315
+ stderr?: OutputWriter;
62
316
  }
63
- type UsageOutput = Required<Pick<ShowUsageOptions, "write">>;
64
- type ShowUsageCallback = (command: AnyCommandDef, output: UsageOutput) => void | Promise<void>;
65
- /** Generate usage text without writing to stdout or stderr. */
66
- declare function renderUsage(command: AnyCommandDef): Promise<string>;
67
- /** Generate and write usage text, awaiting asynchronous writers. */
68
- declare function showUsage(command: AnyCommandDef, options?: ShowUsageOptions): Promise<void>;
317
+ /** Create the fixed human-readable renderer used for pretty output. */
318
+ declare function createPrettyPrinter(options: PrettyPrinterOptions): Printer;
69
319
  //#endregion
70
320
  //#region src/run.d.ts
71
- declare function runCommand<S extends ArgsSchema>(root: CommandDef<S>, argv: string[]): Promise<unknown>;
72
- interface RunMainOptions {
321
+ interface RunCommandOptions extends OutputOptions {
322
+ printer?: Printer;
323
+ showUsage?: ShowUsageCallback;
324
+ }
325
+ declare function runCommand<S extends ArgsSchema>(root: CommandDef<S>, argv: string[], options?: RunCommandOptions): Promise<unknown>;
326
+ interface RunMainOptions extends OutputOptions {
73
327
  onError?: (error: unknown) => void | Promise<void>;
74
328
  argv?: string[];
329
+ printer?: Printer;
75
330
  showUsage?: ShowUsageCallback;
76
331
  }
77
332
  declare function runMain<S extends ArgsSchema>(command: CommandDef<S>, options?: RunMainOptions): Promise<void>;
78
333
  declare function createMain<S extends ArgsSchema>(command: CommandDef<S>, options?: RunMainOptions): (argv?: string[]) => Promise<void>;
79
334
  //#endregion
80
- export { type ArgOptions, type ArgOptionsMap, type CommandContext, type CommandDef, type RunMainOptions, type ShowUsageCallback, type ShowUsageOptions, type UsageOutput, type UsageWriter, type ZittyIssue, ZittyUsageError, ZittyValidationError, createMain, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage };
335
+ export { type ArgOptions, type ArgOptionsMap, type CommandContext, type CommandDef, type ConfirmOptions, type ConfirmPromptState, type ErrorPrintEvent, type HackshErrorKind, type HackshErrorSummary, HackshPromptError, type HackshPromptErrorReason, type JsonPrinterOptions, type LogKind, type LogLevel, type LogPrintEvent, type LogProperties, type LogRecord, type Logger, type OutputFormat, type OutputMode, type OutputOptions, type OutputPreference, type OutputWriter, type PrettyPrinterOptions, type PrintEvent, type PrintStream, type Printer, type PromptChoiceState, type PromptErrorDetails, type PromptKind, type PromptPrintEvent, type PromptState, type PromptStatus, type Prompter, type ResolveOutputModeOptions, type RunCommandOptions, type RunMainOptions, type SelectOption, type SelectOptions, type SelectPromptState, type ShowUsageCallback, type ShowUsageOptions, type StreamPrinterOptions, type TerminalInput, type TerminalOutput, type TextOptions, type TextPromptState, type UsageArgumentSummary, type UsageCommandSummary, type UsageOptionSummary, type UsageOutput, type UsagePrintEvent, type UsageSummary, type UsageWriter, type ValidationIssueSummary, type VersionPrintEvent, type ZittyIssue, ZittyUsageError, ZittyValidationError, consolePrinter, createJsonPrinter, createMain, createPrettyPrinter, createStreamPrinter, defineCommand, parseArgs, renderUsage, resolveOutputMode, runCommand, runMain, showUsage, summarizeUsage };
81
336
  //# sourceMappingURL=index.d.ts.map