@effected/cli 0.10.0 → 0.11.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.
Files changed (89) hide show
  1. package/Cancelled.js +44 -0
  2. package/CliAudience.js +178 -0
  3. package/CliColor.js +13 -19
  4. package/CliEnv.js +89 -0
  5. package/CliExit.js +1 -1
  6. package/CliFailure.js +253 -0
  7. package/CliInteractive.js +71 -0
  8. package/CliLinks.js +154 -0
  9. package/CliLog.js +294 -0
  10. package/CliLogger.js +34 -33
  11. package/CliMessage.js +83 -0
  12. package/CliPrompt.js +104 -0
  13. package/CliRuntime.js +103 -53
  14. package/CliTest.js +16 -0
  15. package/CliTheme.js +128 -0
  16. package/ConfigIssueRenderer.js +14 -33
  17. package/Doc.js +512 -0
  18. package/Fmt.js +133 -0
  19. package/GithubAnnotation.js +40 -0
  20. package/Glyphs.js +83 -0
  21. package/NotInteractive.js +42 -0
  22. package/README.md +129 -131
  23. package/Render.js +254 -0
  24. package/SchemaIssueRenderer.js +7 -10
  25. package/Status.js +163 -0
  26. package/TestTerminal.js +80 -0
  27. package/Token.js +69 -0
  28. package/index.d.ts +2923 -171
  29. package/index.js +19 -1
  30. package/internal/ansi.js +230 -0
  31. package/internal/autoFormat.js +34 -0
  32. package/internal/canPrompt.js +15 -0
  33. package/internal/counts.js +69 -0
  34. package/internal/diagnostics.js +32 -0
  35. package/internal/displayWidth.js +35 -0
  36. package/internal/failureTarget.js +156 -0
  37. package/internal/fallbackAnswer.js +18 -0
  38. package/internal/fileSink.js +62 -0
  39. package/internal/format.js +62 -7
  40. package/internal/layout.js +250 -0
  41. package/internal/linkScheme.js +30 -0
  42. package/internal/linkTarget.js +50 -0
  43. package/internal/logSafety.js +46 -0
  44. package/internal/renderAnsi.js +52 -0
  45. package/internal/renderDoc.js +319 -0
  46. package/internal/renderGithubLog.js +46 -0
  47. package/internal/renderMarkdown.js +367 -0
  48. package/internal/renderPlain.js +50 -0
  49. package/internal/scanAudience.js +106 -0
  50. package/internal/splitFrame.js +56 -0
  51. package/internal/wizardGate.js +18 -0
  52. package/package.json +35 -5
  53. package/testing.d.ts +88 -2
  54. package/testing.js +2 -1
  55. package/ui/CliUi.js +348 -0
  56. package/ui/CliUiLive.js +399 -0
  57. package/ui/Confirm.js +245 -0
  58. package/ui/DocView.js +74 -0
  59. package/ui/KeyHelp.js +62 -0
  60. package/ui/KeyTable.js +199 -0
  61. package/ui/MultiSelect.js +260 -0
  62. package/ui/Select.js +226 -0
  63. package/ui/Tabs.js +202 -0
  64. package/ui/TextInput.js +250 -0
  65. package/ui/Toggle.js +32 -0
  66. package/ui/UiKey.js +44 -0
  67. package/ui/UiProvider.js +60 -0
  68. package/ui/UiStreams.js +18 -0
  69. package/ui/UiTheme.js +119 -0
  70. package/ui/Viewport.js +204 -0
  71. package/ui/internal/ErrorBoundary.js +30 -0
  72. package/ui/internal/Holder.js +74 -0
  73. package/ui/internal/ScreenContext.js +52 -0
  74. package/ui/internal/UiProviders.js +21 -0
  75. package/ui/internal/ink.js +122 -0
  76. package/ui/internal/inkChalk.js +58 -0
  77. package/ui/internal/inkConsole.js +146 -0
  78. package/ui/internal/lineText.js +19 -0
  79. package/ui/internal/mountPermit.js +16 -0
  80. package/ui/internal/perfDrain.js +33 -0
  81. package/ui/internal/processStreams.js +19 -0
  82. package/ui/internal/renderOptions.js +13 -0
  83. package/ui/testing/CliUiTest.js +735 -0
  84. package/ui/testing/fakeStreams.js +76 -0
  85. package/ui/testing/terminalModel.js +59 -0
  86. package/ui-testing.d.ts +446 -0
  87. package/ui-testing.js +3 -0
  88. package/ui.d.ts +1648 -0
  89. package/ui.js +17 -0
package/index.d.ts CHANGED
@@ -1,54 +1,2032 @@
1
- import { Cause, Context, Effect, Layer, LogLevel, Logger, MutableRef, Stdio } from "effect";
2
- import { CliOutput } from "effect/cli";
1
+ import { Array as Array$1, Cause, Context, Effect, FileSystem, Layer, LogLevel, Logger, MutableRef, Option, Path, Runtime, Schema, Stdio, Terminal } from "effect";
2
+ import { Audience, AudienceKind, ColorLevel, CurrentRuntimeEnv, RuntimeEnv, TerminalEnv } from "@effected/env";
3
+ import { CliError, CliOutput, Command, Flag, Param, Prompt } from "effect/cli";
3
4
  import { ConfigValidationError } from "@effected/config-file";
5
+ //#region src/Cancelled.d.ts
6
+ declare const Cancelled_base: Schema.Class<Cancelled, Schema.TaggedStruct<"Cancelled", {
7
+ readonly reason: Schema.Literals<readonly ["escape", "interrupt"]>;
8
+ }>, import("effect/Cause").YieldableError>;
9
+ /**
10
+ * A person backed out of an interactive prompt: they pressed escape, or the
11
+ * prompt was interrupted.
12
+ *
13
+ * @remarks
14
+ * Exits `130`, the conventional status for a run ended by the user, through
15
+ * core's own `Runtime.errorExitCode` marker, so `CliRuntime.reportFailures`
16
+ * keeps it. Its default rendering is one line, `cancelled; nothing written`,
17
+ * because nothing has been written by the time a prompt is cancelled and a
18
+ * stack trace would only alarm. A consumer `render` still overrides the line, and can hand off to it: the line is
19
+ * the error's `message`, so `error.message` and `String(error)` carry it.
20
+ *
21
+ * @public
22
+ */
23
+ export declare class Cancelled extends Cancelled_base {
24
+ /**
25
+ * The one line, `cancelled; nothing written`.
26
+ *
27
+ * @remarks
28
+ * A prototype getter, not a field, so it is not part of the encoded form, equality or a JSON dump. Assigning to
29
+ * it is ignored: a library that rewrites `error.message` (to prefix a context, say) must not make this error throw,
30
+ * which a getter-only property does in strict mode. The line is fixed.
31
+ */
32
+ get message(): string;
33
+ set message(_value: string);
34
+ /**
35
+ * The process exit code: `130`.
36
+ *
37
+ * @remarks
38
+ * A prototype getter rather than an own field, so a JSON or logger dump of the error does not carry the
39
+ * runtime marker.
40
+ */
41
+ get [Runtime.errorExitCode](): number;
42
+ }
43
+ //#endregion
44
+ //#region src/CliAudience.d.ts
45
+ /**
46
+ * The four parsed audience flags a root command carries once {@link CliAudience.flags} is shared onto it.
47
+ *
48
+ * @public
49
+ */
50
+ interface AudienceFlagInput {
51
+ /** Every `--audience <kind>` occurrence. */
52
+ readonly audience: ReadonlyArray<AudienceKind>;
53
+ /** Every `--human` occurrence. */
54
+ readonly human: ReadonlyArray<boolean>;
55
+ /** Every `--agent` occurrence. */
56
+ readonly agent: ReadonlyArray<boolean>;
57
+ /** Every `--ci` occurrence. */
58
+ readonly ci: ReadonlyArray<boolean>;
59
+ }
60
+ /**
61
+ * Options for {@link CliAudience.flags}.
62
+ *
63
+ * @public
64
+ */
65
+ interface CliAudienceFlagsOptions {
66
+ /**
67
+ * Hide all four flags from every help screen. They still parse and resolve; describe them in the root
68
+ * command's description instead.
69
+ */
70
+ readonly hidden?: boolean | undefined;
71
+ }
72
+ /**
73
+ * Resolves to nothing for a command that carries the four audience flags, and to an unsatisfiable marker otherwise,
74
+ * so `CliAudience.run` on a root that forgot `Command.withSharedFlags(CliAudience.flags())` does not compile. (A
75
+ * plain `Input extends AudienceFlagInput` constraint does not do this: `Command` is contravariant in its input, so
76
+ * a command with no flags still type-checks against it.)
77
+ *
78
+ * @public
79
+ */
80
+ type RequiresAudienceFlags<Input> = [Input] extends [AudienceFlagInput] ? unknown : {
81
+ readonly "missing shared flags": "pipe the root through Command.withSharedFlags(CliAudience.flags())";
82
+ };
83
+ /**
84
+ * The audience flags of a CLI: `--audience <human|agent|ci>` and the shorthands `--human`, `--agent`, `--ci`,
85
+ * resolved into `@effected/env`'s `Audience`.
86
+ *
87
+ * @remarks
88
+ * The one wiring: share the flags on the root command and hand the root to {@link CliAudience.run} (or
89
+ * {@link CliAudience.runWith}), which resolves the flags from argv before core parses and applies
90
+ * {@link CliAudience.provide} itself:
91
+ *
92
+ * ```ts
93
+ * const root = Command.make("tool").pipe(
94
+ * Command.withSharedFlags(CliAudience.flags()),
95
+ * Command.withSubcommands([verify]),
96
+ * )
97
+ * NodeRuntime.runMain(
98
+ * CliRuntime.main(CliAudience.run(root, { version }), { platform: NodeServices.layer, env: {} }),
99
+ * )
100
+ * ```
101
+ *
102
+ * Core lists a shared flag in the help of every subcommand, not only at the root: that is upstream (Effect-TS/effect
103
+ * issue 8642), and `flags({ hidden: true })` is the way to keep them out of help altogether.
104
+ *
105
+ * A root that forgot `Command.withSharedFlags(CliAudience.flags())` does not compile. Giving more than one
106
+ * occurrence across the four flags is a usage error even when they agree; a boolean set to false (`--no-agent`,
107
+ * `--agent=false`) counts as not given. A bad `--audience` value is core's own parse error. Both exit `64` under
108
+ * `CliRuntime.main`. A conflicting audience together with `--help` exits `0` and prints help, because core handles
109
+ * its action flags before the resolver runs.
110
+ *
111
+ * A flag decides `CliInteractive` from the audience it names and the terminal facts: `--human` is interactive when
112
+ * `TerminalEnv` says there is a terminal on stdin and on stdout, even where the environment detected an agent, so a
113
+ * person running the tool inside an agent can ask for the human experience; in a pipe it still cannot prompt. A
114
+ * non-human flag (`--agent`, `--ci`, `--audience agent|ci`) turns it off and drops `--wizard`, and switches
115
+ * diagnostics to NDJSON, for the whole run including the parse step where a fallback prompt fires. Without
116
+ * `TerminalEnv` in the environment a flag only narrows. `--wizard` follows the decision: a run a flag makes
117
+ * interactive gets it back where the environment's gate had dropped it, and only there: a consumer's own `builtIns`
118
+ * without it stay without it.
119
+ * {@link CliAudience.provide} on its own, the path for a bare `Command.run`, acts only on the subcommand handler,
120
+ * because core parses the root flags into a local context before any of it is visible.
121
+ *
122
+ * @public
123
+ */
124
+ export declare class CliAudience {
125
+ private constructor();
126
+ /**
127
+ * The four flags, for `Command.withSharedFlags` on the root command.
128
+ *
129
+ * @remarks
130
+ * Each is repeatable, so every occurrence is counted; a boolean given as `false` (`--agent=false`,
131
+ * `--no-agent`) is not an occurrence. Core lists shared flags in every command's help; pass
132
+ * `hidden` to remove them from all of them.
133
+ */
134
+ static readonly flags: (options?: CliAudienceFlagsOptions) => {
135
+ readonly audience: Flag.Flag<ReadonlyArray<AudienceKind>>;
136
+ readonly human: Flag.Flag<ReadonlyArray<boolean>>;
137
+ readonly agent: Flag.Flag<ReadonlyArray<boolean>>;
138
+ readonly ci: Flag.Flag<ReadonlyArray<boolean>>;
139
+ };
140
+ /**
141
+ * Resolve the flags before every subcommand handler and re-provide `Audience`.
142
+ *
143
+ * @remarks
144
+ * `CliAudience.run` and `runWith` apply this themselves, so a program run through them never needs it. Use it
145
+ * directly only with a bare `Command.run`, which leaves a fallback prompt blind to the flags (see the class
146
+ * remarks).
147
+ *
148
+ * A failure report is written outside the run, where the flag is not in force, so only `runWith` and `run` carry
149
+ * the flag's audience to it; with this on its own the report follows the environment's audience.
150
+ *
151
+ * Pipe it onto the composite root, after `withSubcommands`, since a parent's handler does not run when a
152
+ * subcommand is selected. With exactly one flag the audience is `{ kind, source: "flag" }`; with none the
153
+ * ambient `Audience` is read and provided back unchanged, so `Audience` stays in the requirement a handler
154
+ * reading it already has, and is added to a program whose handlers do not read it.
155
+ */
156
+ static readonly provide: <const Name extends string, Input extends AudienceFlagInput, ContextInput, E, R>(command: Command.Command<Name, Input, ContextInput, E, R>) => Command.Command<Name, Input, ContextInput, E | CliError.UserError, Exclude<R, Audience> | Audience>;
157
+ /**
158
+ * Run a command the way `Command.runWith` does, with the audience flag resolved BEFORE core parses.
159
+ *
160
+ * @remarks
161
+ * It scans `argv` for the four audience flags first, then runs core around a provided `Audience` (when exactly
162
+ * one is given: `{ kind, source: "flag" }`) and a `CliInteractive` decided from it: `--human` is interactive when
163
+ * `TerminalEnv` reports a terminal on stdin and stdout and `TERM` is not `dumb` (it can turn prompting on under a detected agent), a
164
+ * non-human flag or a conflict makes it false. A fallback prompt fires while core parses, earlier than
165
+ * anything `CliAudience.provide` can reach, so `--agent init` on a terminal would otherwise still prompt. No
166
+ * flag leaves the ambient values untouched. A conflict still gets core's own usage error, exit `64`, from
167
+ * `CliAudience.provide`'s resolver.
168
+ *
169
+ * @param command - the composite root, with the flags shared and `CliAudience.provide` piped on
170
+ * @param config - the same `version` and `renderErrors` as core's
171
+ */
172
+ static readonly runWith: <const Name extends string, Input, E, R, ContextInput>(command: Command.Command<Name, Input, ContextInput, E, R> & RequiresAudienceFlags<Input>, config: {
173
+ readonly version: string;
174
+ readonly renderErrors?: boolean | undefined;
175
+ }) => ((input: ReadonlyArray<string>) => Effect.Effect<void, Exclude<E | CliError.UserError, Terminal.QuitError> | CliError.CliError, Exclude<R, Audience> | Audience | Command.Environment>);
176
+ /**
177
+ * `Command.run` with the audience flag resolved before parsing: reads `Stdio.args` and calls
178
+ * {@link CliAudience.runWith}.
179
+ *
180
+ * @param command - the composite root
181
+ * @param config - the same `version` and `renderErrors` as core's
182
+ */
183
+ static readonly run: <const Name extends string, Input, E, R, ContextInput>(command: Command.Command<Name, Input, ContextInput, E, R> & RequiresAudienceFlags<Input>, config: {
184
+ readonly version: string;
185
+ readonly renderErrors?: boolean | undefined;
186
+ }) => Effect.Effect<void, Exclude<E | CliError.UserError, Terminal.QuitError> | CliError.CliError, Exclude<R, Audience> | Audience | Command.Environment>;
187
+ }
188
+ //#endregion
4
189
  //#region src/CliColor.d.ts
5
190
  /**
6
- * Whether a CLI's output should carry ANSI colour, decided once and shared by
7
- * everything that renders — help text, error output, and any rendered result.
191
+ * Whether a CLI's output should carry ANSI colour, decided once and shared by
192
+ * everything that renders — help text, error output, and any rendered result.
193
+ *
194
+ * @remarks
195
+ * The decision is `@effected/env`'s `TerminalEnv.colorLevel("stdout")`, which
196
+ * follows Node's `getColorDepth` precedence: `FORCE_COLOR` first (`0` or an
197
+ * unrecognised value forces colour off; an empty value, `1` or `true` force basic colour, `2` 256 colours and
198
+ * `3` truecolor, even without a terminal), then a non-empty `NO_COLOR` or `NODE_DISABLE_COLORS` and
199
+ * `TERM=dumb`, then the TTY gate. `FORCE_COLOR` therefore beats `NO_COLOR`.
200
+ * The environment
201
+ * is read through the ambient `ConfigProvider`, never `process`, so a test
202
+ * swaps it with `Effect.provideService(ConfigProvider.ConfigProvider, ...)`;
203
+ * an ambient `TerminalEnv`, such as `TerminalEnv.layerTest`, answers instead
204
+ * when one is provided.
205
+ *
206
+ * @public
207
+ */
208
+ export declare class CliColor {
209
+ private constructor();
210
+ /**
211
+ * The decision, for passing to a pure renderer as a plain boolean.
212
+ *
213
+ * @public
214
+ */
215
+ static readonly enabled: Effect.Effect<boolean, never, Stdio.Stdio>;
216
+ /**
217
+ * Core's default `CliOutput.Formatter`, coloured by the same decision as
218
+ * {@link CliColor.enabled}, so help text, parse errors and rendered
219
+ * output never disagree on whether colour is on.
220
+ *
221
+ * @remarks
222
+ * `overrides` replaces individual methods of the default formatter — for
223
+ * example `formatVersion`, to append a "via `<carrier>`" line without
224
+ * losing the other defaults. Each call mints a fresh layer; bind the
225
+ * result to a `const` or the decision is re-read every time it is
226
+ * provided.
227
+ *
228
+ * The `never` in its output does not mean it installs nothing.
229
+ * `CliOutput.Formatter` is a `Context.Reference`, whose key type is
230
+ * `never`, so this layer sets the formatter reference rather than
231
+ * providing a service. Every command it is provided to renders with the
232
+ * formatter it sets, and without it they fall back to core's default
233
+ * formatter.
234
+ *
235
+ * @public
236
+ */
237
+ static readonly formatterLayer: (overrides?: Partial<CliOutput.Formatter>) => Layer.Layer<never, never, Stdio.Stdio>;
238
+ }
239
+ //#endregion
240
+ //#region src/Glyphs.d.ts
241
+ /**
242
+ * The symbols a theme draws with.
243
+ *
244
+ * @public
245
+ */
246
+ interface GlyphSet {
247
+ /** Which set this is. */
248
+ readonly kind: "unicode" | "ascii";
249
+ /** The truncation marker. */
250
+ readonly ellipsis: string;
251
+ /** The frames of a spinner, in order. */
252
+ readonly spinner: ReadonlyArray<string>;
253
+ /** A list bullet. */
254
+ readonly bullet: string;
255
+ /** A directional arrow. */
256
+ readonly arrow: string;
257
+ /** The separator between the segments of a breadcrumb or path: one for people, one for agents. */
258
+ readonly pathSeparator: {
259
+ readonly human: string;
260
+ readonly agent: string;
261
+ };
262
+ /** How long a spinner frame is shown, in milliseconds. */
263
+ readonly spinnerIntervalMs: number;
264
+ /**
265
+ * The segments a tree is drawn with: `branch` before a child that has later siblings, `last` before the final
266
+ * child, and `pipe` and `blank` as the indent under each. All four are one width so branches align.
267
+ */
268
+ readonly tree: {
269
+ readonly branch: string;
270
+ readonly last: string;
271
+ readonly pipe: string;
272
+ readonly blank: string;
273
+ };
274
+ }
275
+ /**
276
+ * Options for {@link Glyphs.select}.
277
+ *
278
+ * @public
279
+ */
280
+ interface GlyphSelectOptions {
281
+ /**
282
+ * `true` is ASCII, `false` is Unicode, and `auto` (the default) is ASCII only when `term` is `dumb`.
283
+ */
284
+ readonly ascii?: boolean | "auto" | undefined;
285
+ /**
286
+ * The `TERM` value, which only `auto` reads. It is passed in rather than read: this selection is pure, so a
287
+ * caller with no Effect context (an Ink component) uses it, and `process` is never consulted. Unset is not
288
+ * `dumb`.
289
+ */
290
+ readonly term?: string | undefined;
291
+ }
292
+ /**
293
+ * The two glyph sets: Unicode, and a plain-ASCII fallback for terminals that cannot draw it.
294
+ *
295
+ * @remarks
296
+ * The sets are shared, so they and their nested values are frozen.
297
+ *
298
+ * @public
299
+ */
300
+ export declare class Glyphs {
301
+ private constructor();
302
+ /** Unicode symbols. */
303
+ static readonly unicode: GlyphSet;
304
+ /**
305
+ * Pick a glyph set without a service: the one `CliTheme` uses, as a pure function.
306
+ *
307
+ * @remarks
308
+ * `CliTheme.layer` reads `TERM` through `Config` and calls this, so the two agree. `StreamEnv` carries no
309
+ * `TERM`, and nothing else in it decides ASCII, so the caller passes `term` when it wants `auto` to mean
310
+ * something.
311
+ *
312
+ * @param options - whether to force ASCII or Unicode, and the `TERM` value `auto` reads
313
+ */
314
+ static readonly select: (options?: GlyphSelectOptions) => GlyphSet;
315
+ /** ASCII-only symbols. */
316
+ static readonly ascii: GlyphSet;
317
+ }
318
+ //#endregion
319
+ //#region src/Token.d.ts
320
+ /**
321
+ * A named terminal colour: the eight ANSI colours and their bright variants.
322
+ *
323
+ * @remarks
324
+ * The bright variants are spelled as chalk and Ink spell them (`redBright`, `blackBright`), so a style maps to
325
+ * either without a rename table. `gray` is chalk's alias for `blackBright`.
326
+ *
327
+ * @public
328
+ */
329
+ type NamedColor = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright" | "gray";
330
+ /**
331
+ * A terminal style: an optional foreground colour and text attributes.
332
+ *
333
+ * @remarks
334
+ * A style is data; it carries no escape sequences. `CliTheme.paint` renders it for the terminal's colour
335
+ * level, and at level `none` rendering is the identity. A hex foreground that is not `#rgb` or `#rrggbb`, and
336
+ * a name that is not a {@link NamedColor}, is ignored when rendered rather than failing.
337
+ *
338
+ * @public
339
+ */
340
+ interface Style {
341
+ /** The foreground: a named colour or a `#rrggbb` hex. */
342
+ readonly fg?: NamedColor | `#${string}`;
343
+ /** Bold. */
344
+ readonly bold?: boolean;
345
+ /** Dim, or faint. */
346
+ readonly dim?: boolean;
347
+ /** Italic. */
348
+ readonly italic?: boolean;
349
+ /** Underline. */
350
+ readonly underline?: boolean;
351
+ }
352
+ /**
353
+ * The semantic tokens a theme resolves to a {@link Style}.
354
+ *
355
+ * @public
356
+ */
357
+ type TokenName = "success" | "failure" | "warning" | "info" | "error" | "muted" | "accent" | "emphasis";
358
+ /**
359
+ * Constructors for {@link Style} values, and the pure resolution of a token to one.
360
+ *
361
+ * @public
362
+ */
363
+ export declare class Token {
364
+ private constructor();
365
+ /**
366
+ * The default {@link Style} of every token, frozen.
367
+ *
368
+ * @remarks
369
+ * Data, not a service: it is what `CliTheme` starts from, and a renderer with no Effect context (an Ink
370
+ * component) reads it directly.
371
+ */
372
+ static readonly defaults: Readonly<Record<TokenName, Style>>;
373
+ /**
374
+ * The style a token or style resolves to, as a pure function: no service, no terminal.
375
+ *
376
+ * @remarks
377
+ * An explicit style resolves to itself. A token name resolves to its override when `overrides` has one, else
378
+ * its default; a name that is not a token (including an `Object.prototype` member) resolves to the empty
379
+ * style. This is the same resolution `CliTheme.paint` applies, which is what `StreamTheme.style` reports.
380
+ *
381
+ * @param token - a token name or an explicit style
382
+ * @param overrides - styles that replace the default of a token, as `CliThemeOptions.tokens` does
383
+ */
384
+ static readonly resolve: (token: TokenName | Style, overrides?: Partial<Record<TokenName, Style>>) => Style;
385
+ /**
386
+ * A foreground from a hex colour.
387
+ *
388
+ * @param hex - `#rrggbb` (or `#rgb`)
389
+ */
390
+ static readonly hex: (hex: `#${string}`) => Style;
391
+ /**
392
+ * A foreground from a named colour.
393
+ *
394
+ * @param color - the colour
395
+ */
396
+ static readonly named: (color: NamedColor) => Style;
397
+ /**
398
+ * A style, as written. Exists so a style reads as a token at a call site.
399
+ *
400
+ * @param style - the style
401
+ */
402
+ static readonly style: (style: Style) => Style;
403
+ }
404
+ //#endregion
405
+ //#region src/Status.d.ts
406
+ /**
407
+ * How one status looks: a Unicode glyph, an ASCII fallback, a token and a rank.
408
+ *
409
+ * @public
410
+ */
411
+ interface StatusDef {
412
+ /** The Unicode glyph. */
413
+ readonly glyph: string;
414
+ /** The ASCII fallback, used when the theme's glyphs are ASCII. */
415
+ readonly ascii: string;
416
+ /** The semantic token, or an explicit style, the glyph is painted with. */
417
+ readonly token: TokenName | Style;
418
+ /** Severity for {@link Status.worst}: the highest rank wins. */
419
+ readonly rank: number;
420
+ }
421
+ /**
422
+ * The names of the core vocabulary.
423
+ *
424
+ * @public
425
+ */
426
+ type CoreStatusName = "success" | "failure" | "warning" | "info" | "skip" | "pending";
427
+ /**
428
+ * An open vocabulary of statuses.
429
+ *
430
+ * @remarks
431
+ * Start from `Status.core` and add your own with `extend`. The names are a type parameter,
432
+ * so `def` and `worst` reject a name the vocabulary does not have at compile time.
433
+ *
434
+ * @example
435
+ * ```ts
436
+ * import { Status } from "@effected/cli"
437
+ *
438
+ * const vocab = Status.extend({
439
+ * timeout: { glyph: "⏱", ascii: "[time]", token: "warning", rank: 85 },
440
+ * })
441
+ * const worst = vocab.worst(["success", "timeout"])
442
+ * // => "timeout"
443
+ * ```
444
+ *
445
+ * @public
446
+ */
447
+ export declare class Status<Names extends string> {
448
+ private readonly defs;
449
+ private constructor();
450
+ /** The core vocabulary: success, skip, pending, info, warning and failure, by rank 10, 20, 30, 40, 60, 90. */
451
+ static readonly core: Status<CoreStatusName>;
452
+ /**
453
+ * The core vocabulary plus `extra`; an entry that reuses a core name replaces it.
454
+ *
455
+ * @param extra - the statuses to add, by name
456
+ */
457
+ static readonly extend: <const Extra extends Record<string, StatusDef>>(extra: Extra) => Status<CoreStatusName | (keyof Extra & string)>;
458
+ /**
459
+ * This vocabulary plus `extra`; an entry that reuses a name replaces it.
460
+ *
461
+ * @remarks
462
+ * An entry may replace a core name. Replacing `warning` with a lower rank moves the threshold at which
463
+ * `CliMessage.status` defaults to stderr for this vocabulary, since that threshold is `warning`'s rank in the
464
+ * vocabulary it is given.
465
+ *
466
+ * @param extra - the statuses to add, by name
467
+ */
468
+ extend<const Extra extends Record<string, StatusDef>>(extra: Extra): Status<Names | (keyof Extra & string)>;
469
+ /**
470
+ * The definition of a status.
471
+ *
472
+ * @remarks
473
+ * A name the vocabulary does not have is a defect: it throws an `Error` naming it and the names that exist. The
474
+ * types already reject one, so it is reachable only through a cast.
475
+ *
476
+ * @param name - a name in this vocabulary
477
+ */
478
+ def(name: Names): StatusDef;
479
+ /**
480
+ * The full definition of a status as an immutable snapshot, for a caller that stores it.
481
+ *
482
+ * @remarks
483
+ * `def` answers the vocabulary's own entry. `resolve` answers a frozen copy, so a document node that holds
484
+ * the definition stays plain data and editing it cannot change the vocabulary. The copy is shallow: a
485
+ * `token` given as a `Style` keeps its own identity. Throws on an unknown name, as {@link Status.def} does;
486
+ * storing an empty definition in a document instead would fail far from the cause.
487
+ *
488
+ * @param name - a name in this vocabulary
489
+ */
490
+ resolve(name: Names): StatusDef;
491
+ /**
492
+ * A status's glyph from a glyph set: `def.ascii` for an ASCII set, `def.glyph` otherwise. Unpainted, for a caller
493
+ * that draws it itself (an Ink tree, a reporter).
494
+ *
495
+ * @remarks
496
+ * Throws on an unknown name, as {@link Status.def} does.
497
+ *
498
+ * @param name - a name in this vocabulary
499
+ * @param glyphs - the glyph set, such as `Glyphs.unicode`, `Glyphs.ascii` or a theme's
500
+ */
501
+ glyph(name: Names, glyphs: GlyphSet): string;
502
+ /**
503
+ * The status with the highest rank; a tie goes to the one that comes first in `names`.
504
+ *
505
+ * @remarks
506
+ * `rank` is SEVERITY, not an aggregation policy: the higher rank wins, so in `Status.core` a `skip` outranks a
507
+ * `success`. A consumer whose aggregate differs (a test run where passes dominate skips, say) folds its own
508
+ * rule over the names instead of reading this.
509
+ *
510
+ * Takes at least one name, so the answer is always a name. For an array that may be empty, use
511
+ * {@link Status.worstOption}. They are two methods because a literal and an array variable are the same
512
+ * array at runtime, so one method could not return a name for one and an `Option` for the other.
513
+ *
514
+ * @param names - the statuses to compare
515
+ */
516
+ worst(names: Array$1.NonEmptyReadonlyArray<Names>): Names;
517
+ /**
518
+ * The status with the highest rank of an array that may be empty: `None` when it is, otherwise `Some` of
519
+ * the worst, a tie going to the one that comes first in `names`. Rank is severity, not an aggregation policy; see
520
+ * {@link Status.worst}.
521
+ *
522
+ * @param names - the statuses to compare
523
+ */
524
+ worstOption(names: ReadonlyArray<Names>): Option.Option<Names>;
525
+ }
526
+ //#endregion
527
+ //#region src/CliTheme.d.ts
528
+ /**
529
+ * The shape of the {@link CliTheme} service: a colour level, a glyph set and the functions that use them.
530
+ *
531
+ * @remarks
532
+ * The whole shape is one immutable value with no mockable behaviour, so `Layer.succeed` (via
533
+ * {@link CliTheme.layerTest}) is the complete double.
534
+ *
535
+ * @public
536
+ */
537
+ interface CliThemeShape extends StreamTheme {
538
+ /**
539
+ * The theme of one output stream, painting with THAT stream's colour level.
540
+ *
541
+ * @remarks
542
+ * The members above are the `stdout` ones. Anything written to stderr must be painted through
543
+ * `forStream("stderr")`: redirecting one stream (`tool 2>err.log`, `tool | jq`) changes that stream's colour
544
+ * and not the other's.
545
+ */
546
+ readonly forStream: (stream: "stdout" | "stderr") => StreamTheme;
547
+ }
548
+ /**
549
+ * A theme bound to one stream's colour level.
550
+ *
551
+ * @public
552
+ */
553
+ interface StreamTheme {
554
+ /** Render `text` in a token or an explicit style; the identity when colour is `none`. */
555
+ readonly paint: (token: TokenName | Style, text: string) => string;
556
+ /**
557
+ * The resolved {@link Style} of a token or style: the one `paint` renders, whatever the colour level.
558
+ *
559
+ * @remarks
560
+ * Pure data, for a renderer that is not ANSI (an Ink component maps it to its own props). It applies this
561
+ * theme's token overrides, so it is `Token.resolve` with them.
562
+ */
563
+ readonly style: (token: TokenName | Style) => Style;
564
+ /** The raw opening SGR sequence of a token or style; `""` when colour is `none`. */
565
+ readonly sgr: (token: TokenName | Style) => string;
566
+ /** The glyph set in use. */
567
+ readonly glyphs: GlyphSet;
568
+ /** The colour level of the stream. */
569
+ readonly color: ColorLevel;
570
+ /** Render a status from a vocabulary: its glyph, painted with its token, then `text` when given. */
571
+ readonly status: <N extends string>(vocab: Status<N>, name: N, text?: string) => string;
572
+ }
573
+ /**
574
+ * Options for {@link CliTheme.layer}.
575
+ *
576
+ * @public
577
+ */
578
+ interface CliThemeOptions {
579
+ /** Styles that replace the default of a token. */
580
+ readonly tokens?: Partial<Record<TokenName, Style>> | undefined;
581
+ /** The glyph set. `auto`, the default, is ASCII only when `TERM=dumb`. */
582
+ readonly glyphs?: "unicode" | "ascii" | "auto" | undefined;
583
+ }
584
+ /**
585
+ * Options for {@link CliTheme.layerTest}.
586
+ *
587
+ * @public
588
+ */
589
+ interface CliThemeTestOptions {
590
+ /** The stdout colour level; `none` by default. */
591
+ readonly color?: ColorLevel | undefined;
592
+ /** The stderr colour level; the same as `color` by default. */
593
+ readonly stderrColor?: ColorLevel | undefined;
594
+ /** The glyph set; Unicode by default. */
595
+ readonly glyphs?: "unicode" | "ascii" | undefined;
596
+ }
597
+ declare const CliTheme_base: Context.ServiceClass<CliTheme, "@effected/cli/CliTheme", CliThemeShape>;
598
+ /**
599
+ * The presentation of a CLI: colour tokens, glyphs and statuses, decided once from the terminal.
600
+ *
601
+ * @remarks
602
+ * A `Context.Service`, not a `Reference`. A colour level is a fact about the terminal, not a preference with a
603
+ * safe default, so there is nothing sensible for an unprovided theme to read; requiring it puts `CliTheme` in
604
+ * `R` and a program that forgot to wire it fails to compile rather than printing plain text to a colour
605
+ * terminal. {@link CliTheme.layer} reads `TerminalEnv`.
606
+ *
607
+ * @public
608
+ */
609
+ export declare class CliTheme extends CliTheme_base {
610
+ /**
611
+ * The theme for the terminal `TerminalEnv` describes.
612
+ *
613
+ * @remarks
614
+ * Bind the layer to a constant and provide it once. With `glyphs: "auto"` the glyph set is ASCII only when
615
+ * `TERM=dumb`, read through `Config`.
616
+ *
617
+ * @param options - token overrides and the glyph set
618
+ */
619
+ static readonly layer: (options?: CliThemeOptions) => Layer.Layer<CliTheme, never, TerminalEnv>;
620
+ /**
621
+ * A fixed theme that needs nothing; `none` colour and Unicode glyphs unless told otherwise.
622
+ *
623
+ * @param options - the colour level and glyph set
624
+ */
625
+ static readonly layerTest: (options?: CliThemeTestOptions) => Layer.Layer<CliTheme>;
626
+ /**
627
+ * Sets core's `Prompt.Theme` from the tokens, so built-in prompts match the rest of the output.
628
+ *
629
+ * @remarks
630
+ * The colour fields are raw SGR openers and are empty strings when colour is `none`. Under ASCII glyphs the
631
+ * prompt symbols fall back to ASCII too. A colourless theme is not byte-clean: core's `Ansi.annotate`
632
+ * appends a `\x1b[0m` reset, and prompts write cursor and underline codes, whatever the theme says.
633
+ */
634
+ static readonly promptTheme: Layer.Layer<never, never, CliTheme>;
635
+ }
636
+ //#endregion
637
+ //#region src/Doc.d.ts
638
+ /**
639
+ * A status as a document stores it: its name and its resolved definition.
640
+ *
641
+ * @remarks
642
+ * The definition is stored, not the vocabulary, so a node stays plain data.
643
+ *
644
+ * @public
645
+ */
646
+ interface StatusRef {
647
+ /** The name in the vocabulary it was resolved from. */
648
+ readonly name: string;
649
+ /** The resolved definition. */
650
+ readonly def: StatusDef;
651
+ }
652
+ /**
653
+ * Where a link points: a URL, or a file with an optional position.
654
+ *
655
+ * @public
656
+ */
657
+ type LinkTarget = {
658
+ readonly url: string;
659
+ } | {
660
+ readonly file: string;
661
+ readonly line?: number;
662
+ readonly col?: number;
663
+ };
664
+ /**
665
+ * Content that flows inside a line.
666
+ *
667
+ * @remarks
668
+ * - `Text`: a run of text, optionally painted with a token or style.
669
+ * - `Code`: code in a monospace span.
670
+ * - `Link`: a labelled link to a URL or a file position.
671
+ * - `StatusMark`: a status glyph, carrying its resolved definition.
672
+ * - `Path`: a path or breadcrumb, joined with the audience's path separator.
673
+ * - `Strong` and `Emphasis`: content in bold or italic; markdown `**` and `_`.
674
+ * - `File`: a path shown through the context's `displayPath`, never linked.
675
+ *
676
+ * @public
677
+ */
678
+ type Inline = {
679
+ readonly _tag: "Text";
680
+ readonly value: string;
681
+ readonly token?: TokenName | Style;
682
+ } | {
683
+ readonly _tag: "Code";
684
+ readonly value: string;
685
+ } | {
686
+ readonly _tag: "Link";
687
+ readonly target: LinkTarget;
688
+ readonly label: ReadonlyArray<Inline>;
689
+ /**
690
+ * Whether plain text (and `ansi` with links off, and markdown with no URL) follows the label with the target in
691
+ * parentheses. Unset, it does so only when the label does not already show the target's display form.
692
+ */
693
+ readonly suffix?: boolean;
694
+ } | {
695
+ readonly _tag: "StatusMark";
696
+ readonly name: string;
697
+ readonly def: StatusDef;
698
+ } | {
699
+ readonly _tag: "Path";
700
+ readonly segments: ReadonlyArray<string>;
701
+ } | {
702
+ readonly _tag: "Strong";
703
+ readonly content: ReadonlyArray<Inline>;
704
+ } | {
705
+ readonly _tag: "Emphasis";
706
+ readonly content: ReadonlyArray<Inline>;
707
+ } | {
708
+ readonly _tag: "File";
709
+ readonly path: string;
710
+ };
711
+ /**
712
+ * A node of a {@link TreeNode} tree: a label and its children.
713
+ *
714
+ * @public
715
+ */
716
+ interface TreeNode {
717
+ /** What the node says. */
718
+ readonly label: ReadonlyArray<Inline>;
719
+ /** Its children, in order. */
720
+ readonly children: ReadonlyArray<TreeNode>;
721
+ }
722
+ /**
723
+ * One column of a table.
724
+ *
725
+ * @public
726
+ */
727
+ interface Column {
728
+ /** The header cell. */
729
+ readonly header: ReadonlyArray<Inline>;
730
+ /** How the column's cells align; left when unset. */
731
+ readonly align?: "left" | "right" | "center";
732
+ }
733
+ /**
734
+ * One counter of a `Counts` block.
735
+ *
736
+ * @public
737
+ */
738
+ interface Counter {
739
+ /** A stable identifier, for a caller's total rule. */
740
+ readonly key: string;
741
+ /** What the counter is called when shown. */
742
+ readonly label: string;
743
+ /** The count. */
744
+ readonly n: number;
745
+ /** The status the count is painted with. */
746
+ readonly status: StatusRef;
747
+ /** Show the counter when `n` is zero; by default a zero counter is hidden. */
748
+ readonly showZero?: boolean;
749
+ }
750
+ /**
751
+ * A block of a document.
752
+ *
753
+ * @remarks
754
+ * - `Paragraph`: one logical line; a paragraph of paragraphs is a `Section`.
755
+ * - `List` and `Table`: with an optional `cap` on the rows shown, and an `overflow` that says what the hidden rows
756
+ * amount to, given how many there are.
757
+ * - `Tree`: nested labels.
758
+ * - `Collapsible`: a titled body a renderer may fold.
759
+ * - `Callout`: a body with a kind.
760
+ * - `CodeBlock`: preformatted text, with an optional language.
761
+ * - `Diff`: expected against received text, with an optional cap on the lines shown.
762
+ * - `Section`: children under an optional title.
763
+ * - `Counts`: labelled counters in one of three layouts. `total` replaces the default sum of every counter, and
764
+ * `durationMs` is how long it took. `share: false` drops the headline's share of the total, and `paint` limits what
765
+ * is painted.
766
+ * - `Verbatim`: lines kept exactly, each indented, never wrapped.
767
+ * - `CountsTable`: a table of `Counts` rows, a column per counter key, a `duration` column when some row has one, and
768
+ * an optional summed total row.
769
+ * - `Lines`: one line per entry; markdown keeps them apart with hard breaks.
770
+ * - `Line`: one line, which `truncate` cuts to the width instead of wrapping.
771
+ * - `DiffText`: a unified diff, as given; `truncate` cuts each line to the width.
772
+ * - A `List` may be `compact`, with no blank lines between an item's children (a blank line of an item's own content
773
+ * keeps the item's indent in plain and `ansi`), and a `Table` may be `style: "pipe"`.
774
+ * - `Annotation`: a GitHub Actions annotation, which only `Render.githubLog` writes.
775
+ *
776
+ * Nodes are plain data and nothing decodes them, so a function field such as `overflow` or `total` is fine.
777
+ *
778
+ * @public
779
+ */
780
+ type Block = {
781
+ readonly _tag: "Heading";
782
+ readonly level: 1 | 2 | 3 | 4;
783
+ readonly content: ReadonlyArray<Inline>;
784
+ } | {
785
+ readonly _tag: "Paragraph";
786
+ readonly content: ReadonlyArray<Inline>;
787
+ } | {
788
+ readonly _tag: "List";
789
+ readonly items: ReadonlyArray<Block>;
790
+ readonly cap?: number;
791
+ readonly overflow?: (hidden: number) => ReadonlyArray<Inline>;
792
+ readonly compact?: boolean;
793
+ } | {
794
+ readonly _tag: "Table";
795
+ readonly columns: ReadonlyArray<Column>;
796
+ readonly rows: ReadonlyArray<ReadonlyArray<ReadonlyArray<Inline>>>;
797
+ readonly cap?: number;
798
+ readonly overflow?: (hidden: number) => ReadonlyArray<Inline>;
799
+ readonly style?: "pipe";
800
+ } | {
801
+ readonly _tag: "Tree";
802
+ readonly root: TreeNode;
803
+ } | {
804
+ readonly _tag: "Collapsible";
805
+ readonly title: ReadonlyArray<Inline>;
806
+ readonly body: ReadonlyArray<Block>;
807
+ readonly open?: boolean;
808
+ } | {
809
+ readonly _tag: "Callout";
810
+ readonly kind: "note" | "tip" | "important" | "warning" | "caution";
811
+ readonly body: ReadonlyArray<Block>;
812
+ } | {
813
+ readonly _tag: "CodeBlock";
814
+ readonly lang?: string;
815
+ readonly text: string;
816
+ } | {
817
+ readonly _tag: "Diff";
818
+ readonly expected: string;
819
+ readonly received: string;
820
+ readonly cap?: number;
821
+ } | {
822
+ readonly _tag: "Section";
823
+ readonly title?: ReadonlyArray<Inline>;
824
+ readonly children: ReadonlyArray<Block>;
825
+ } | {
826
+ readonly _tag: "Counts";
827
+ readonly label?: ReadonlyArray<Inline>;
828
+ readonly counters: ReadonlyArray<Counter>;
829
+ readonly total?: (counters: ReadonlyArray<Counter>) => number;
830
+ readonly qualifier?: ReadonlyArray<Inline>;
831
+ readonly durationMs?: number;
832
+ readonly layout: "inline" | "columns" | "row";
833
+ readonly share?: boolean;
834
+ readonly paint?: "all" | "glyph" | "none";
835
+ readonly suffix?: ReadonlyArray<Inline>;
836
+ } | {
837
+ readonly _tag: "CountsTable";
838
+ readonly rows: ReadonlyArray<CountsRow>;
839
+ readonly totalRow?: boolean | ReadonlyArray<Inline>;
840
+ readonly labelHeader?: ReadonlyArray<Inline>;
841
+ readonly durationHeader?: ReadonlyArray<Inline>;
842
+ } | {
843
+ readonly _tag: "Lines";
844
+ readonly lines: ReadonlyArray<ReadonlyArray<Inline>>;
845
+ } | {
846
+ readonly _tag: "Line";
847
+ readonly content: ReadonlyArray<Inline>;
848
+ readonly truncate?: boolean;
849
+ } | {
850
+ readonly _tag: "DiffText";
851
+ readonly text: string;
852
+ readonly cap?: number;
853
+ readonly truncate?: boolean;
854
+ } | {
855
+ readonly _tag: "Verbatim";
856
+ readonly text: string;
857
+ readonly indent?: number;
858
+ } | ({
859
+ readonly _tag: "Annotation";
860
+ readonly message: string;
861
+ } & AnnotationOptions);
862
+ /**
863
+ * Where and how a GitHub Actions annotation is shown: its level, and an optional position and title.
864
+ *
865
+ * @public
866
+ */
867
+ interface AnnotationOptions {
868
+ /** `error`, `warning` or `notice`. */
869
+ readonly level: "error" | "warning" | "notice";
870
+ /** The file it points at, as the runner should show it (relative to the workspace). */
871
+ readonly file?: string;
872
+ /** The line it starts on. */
873
+ readonly line?: number;
874
+ /** The column it starts at. */
875
+ readonly col?: number;
876
+ /** The line it ends on. */
877
+ readonly endLine?: number;
878
+ /** The column it ends at. */
879
+ readonly endColumn?: number;
880
+ /** Its title. */
881
+ readonly title?: string;
882
+ }
883
+ /**
884
+ * One row of a `CountsTable`: its label, its counters and how long it took.
885
+ *
886
+ * @public
887
+ */
888
+ interface CountsRow {
889
+ /** What the row is, such as a project name. */
890
+ readonly label: ReadonlyArray<Inline>;
891
+ /** Its counters; their keys pick the column each lands in. */
892
+ readonly counters: ReadonlyArray<Counter>;
893
+ /** How long it took, in milliseconds, shown with `Fmt.duration` in the duration column. */
894
+ readonly durationMs?: number;
895
+ }
896
+ /**
897
+ * The options of {@link Doc.countsTable}.
898
+ *
899
+ * @public
900
+ */
901
+ interface CountsTableOptions {
902
+ /** A last row summing each column: labelled with a plain `Total` when `true`, or with the content given (`Doc.strong("Total")` for a bold one). */
903
+ readonly totalRow?: boolean | InlineInput;
904
+ /** The header of the label column, such as `Project`; empty when unset. */
905
+ readonly labelHeader?: InlineInput;
906
+ /** The header of the duration column, shown only when some row has a `durationMs`; `duration` when unset. */
907
+ readonly durationHeader?: InlineInput;
908
+ }
909
+ /**
910
+ * Options for {@link Doc.list}.
911
+ *
912
+ * @public
913
+ */
914
+ interface ListOptions extends OverflowOptions {
915
+ /** No blank lines between the children of an item, such as a section's title and body. */
916
+ readonly compact?: boolean;
917
+ }
918
+ /**
919
+ * Options for {@link Doc.table}.
920
+ *
921
+ * @public
922
+ */
923
+ interface TableOptions extends OverflowOptions {
924
+ /**
925
+ * `pipe` gives plain and `ansi` istanbul's shape: rules above and below the header and at the end, cells joined
926
+ * with ` | `. Markdown's table is a pipe table either way. Unset, the columns are space-aligned.
927
+ */
928
+ readonly style?: "pipe";
929
+ }
930
+ /**
931
+ * Options for `Doc.link`.
932
+ *
933
+ * @public
934
+ */
935
+ interface LinkOptions {
936
+ /**
937
+ * Whether the target follows the label in parentheses where a link cannot be followed: plain text, `ansi` with
938
+ * links off, markdown with no URL. `true` always, `false` never; unset, only when the label does not already show
939
+ * the target's display form (`displayPath(file)`, then `:line` and `:col` when present).
940
+ */
941
+ readonly suffix?: boolean;
942
+ }
943
+ /**
944
+ * A whole document: its blocks, in order.
945
+ *
946
+ * @public
947
+ */
948
+ type Document = ReadonlyArray<Block>;
949
+ /**
950
+ * What a constructor accepts for content: a string, one `Inline`, or an array of either.
951
+ *
952
+ * @remarks
953
+ * A string becomes a `Text` node with no token.
954
+ *
955
+ * @public
956
+ */
957
+ type InlineInput = string | Inline | ReadonlyArray<string | Inline>;
958
+ /**
959
+ * A tree node as a constructor accepts it: the label may be a string and `children` may be left out.
960
+ *
961
+ * @public
962
+ */
963
+ interface TreeInput {
964
+ /** What the node says. */
965
+ readonly label: InlineInput;
966
+ /** Its children; none when omitted. */
967
+ readonly children?: ReadonlyArray<TreeInput>;
968
+ }
969
+ /**
970
+ * The inline node with a given `_tag`, so a constructor can return its precise type.
971
+ *
972
+ * @public
973
+ */
974
+ type InlineOf<Tag extends Inline["_tag"]> = Extract<Inline, {
975
+ readonly _tag: Tag;
976
+ }>;
977
+ /**
978
+ * The block node with a given `_tag`, so a constructor can return its precise type.
979
+ *
980
+ * @public
981
+ */
982
+ type BlockOf<Tag extends Block["_tag"]> = Extract<Block, {
983
+ readonly _tag: Tag;
984
+ }>;
985
+ /**
986
+ * The cap and overflow options of a list or table.
987
+ *
988
+ * @public
989
+ */
990
+ interface OverflowOptions {
991
+ /** The most rows to show; the renderer hides the rest. */
992
+ readonly cap?: number;
993
+ /** Says what the hidden rows amount to, given how many there are; it is a function and is never serialised. */
994
+ readonly overflow?: (hidden: number) => InlineInput;
995
+ }
996
+ /**
997
+ * The options of {@link Doc.counts}.
998
+ *
999
+ * @public
1000
+ */
1001
+ interface CountsOptions {
1002
+ /** A leading label. */
1003
+ readonly label?: InlineInput;
1004
+ /** The counters, in the order they are shown. */
1005
+ readonly counters: ReadonlyArray<Counter>;
1006
+ /** Replaces the default total, the sum of `n` over every counter, for example to fold timed-out runs in. */
1007
+ readonly total?: (counters: ReadonlyArray<Counter>) => number;
1008
+ /** Text after the counters, such as `(1 flaky)`. */
1009
+ readonly qualifier?: InlineInput;
1010
+ /** How long it took, in milliseconds. */
1011
+ readonly durationMs?: number;
1012
+ /** How the counters are laid out. */
1013
+ readonly layout: "inline" | "columns" | "row";
1014
+ /** Whether the first counter shows its share of the total, `n/total`; `true` by default. `false` shows `n label`. */
1015
+ readonly share?: boolean;
1016
+ /**
1017
+ * What is painted: `all` (the default) paints the counters, the label, the qualifier and the duration; `glyph`
1018
+ * paints only a status glyph, if one is shown; `none` paints nothing.
1019
+ */
1020
+ readonly paint?: "all" | "glyph" | "none";
1021
+ /** Text after the duration, such as `across 3 files`. */
1022
+ readonly suffix?: InlineInput;
1023
+ }
1024
+ /**
1025
+ * Options for {@link Doc.print}.
1026
+ *
1027
+ * @public
1028
+ */
1029
+ interface DocPrintOptions {
1030
+ /** The stream to write to; `stdout` by default. */
1031
+ readonly stream?: "stdout" | "stderr" | undefined;
1032
+ /**
1033
+ * The renderer. `auto`, the default, is chosen from the audience: `plain` for an agent, `githubLog` for a CI
1034
+ * that `CurrentRuntimeEnv` says is GitHub Actions and `plain` for any other, and `ansi` for a human.
1035
+ */
1036
+ readonly format?: "auto" | "plain" | "ansi" | "markdown" | "githubLog" | undefined;
1037
+ /** Turns an absolute path into its display form, for example relative to the workspace; see `Render.context`. */
1038
+ readonly displayPath?: ((absolute: string) => string) | undefined;
1039
+ /** The display columns to lay out at, replacing the audience's default; see `Render.context`. */
1040
+ readonly width?: number | undefined;
1041
+ }
1042
+ /**
1043
+ * Constructors for the document IR, and two helpers a renderer shares.
1044
+ *
1045
+ * @remarks
1046
+ * Every constructor returns a frozen node and copies the arrays it is given, so editing an input afterwards
1047
+ * cannot change a document. An optional field that is not given is absent from the node, not `undefined`.
1048
+ * Content arguments accept a string, an `Inline` or an array of either.
1049
+ *
1050
+ * A node is plain data: nothing decodes or encodes one, so a function field such as `overflow` or `total` is fine
1051
+ * and a document is not meant to be serialised.
1052
+ *
1053
+ * Freezing covers what a `Doc` constructor builds. A literal you write by hand is not frozen, and a `Style` object
1054
+ * given as a token is shared by reference (the freeze of a status definition is shallow for the same reason).
1055
+ *
1056
+ * @example
1057
+ * ```ts
1058
+ * import { Doc, Status } from "@effected/cli"
1059
+ *
1060
+ * const report = [
1061
+ * Doc.heading(2, "Results"),
1062
+ * Doc.paragraph(Doc.status(Status.core, "success"), " ", "3 checks passed"),
1063
+ * Doc.table([{ header: "Check" }, { header: "Time", align: "right" }], [["lint", "1.2s"]]),
1064
+ * ]
1065
+ * // Written for whoever is reading: `yield* Doc.print(report)`
1066
+ * ```
1067
+ *
1068
+ * @public
1069
+ */
1070
+ export declare class Doc {
1071
+ private constructor();
1072
+ /**
1073
+ * A run of text.
1074
+ *
1075
+ * @param value - the text
1076
+ * @param token - a semantic token or a style to paint it with
1077
+ */
1078
+ static text(value: string, token?: TokenName | Style): InlineOf<"Text">;
1079
+ /**
1080
+ * Code in a monospace span.
1081
+ *
1082
+ * @param value - the code
1083
+ */
1084
+ static code(value: string): InlineOf<"Code">;
1085
+ /**
1086
+ * A link to a URL or a file position.
1087
+ *
1088
+ * @param target - `{ url }` or `{ file, line?, col? }`
1089
+ * @param label - what the link says; when omitted, the bare URL or file path, which leaves out `line` and `col`
1090
+ * @param options - `suffix`, whether the target follows the label where the link cannot be followed
1091
+ */
1092
+ static link(target: LinkTarget, label?: InlineInput, options?: LinkOptions): InlineOf<"Link">;
1093
+ /**
1094
+ * A link when there is a target, and its label alone when there is none: a string label as a `Text`, any other
1095
+ * inline as itself.
1096
+ *
1097
+ * @param target - `{ url }`, `{ file, line?, col? }`, or `undefined` for no link
1098
+ * @param label - what the link says
1099
+ * @param options - `suffix`, whether the target follows the label where the link cannot be followed
1100
+ */
1101
+ static link(target: LinkTarget | undefined, label: string | Inline, options?: LinkOptions): Inline;
1102
+ /**
1103
+ * A status glyph, holding the resolved definition.
1104
+ *
1105
+ * @remarks
1106
+ * A name the vocabulary does not have is a compile error.
1107
+ *
1108
+ * @param vocab - the vocabulary the name belongs to
1109
+ * @param name - a status name in it
1110
+ */
1111
+ static status<N extends string>(vocab: Status<N>, name: NoInfer<N>): InlineOf<"StatusMark">;
1112
+ /**
1113
+ * Content in bold: markdown `**…**`, bold in `ansi`, and the content as is in plain and `githubLog`.
1114
+ *
1115
+ * @param content - any number of strings, inlines or arrays of them, in order
1116
+ */
1117
+ static strong(...content: Array<InlineInput>): InlineOf<"Strong">;
1118
+ /**
1119
+ * Content in italic: markdown `*…*` (which GFM reads inside a word too), italic in `ansi`, and the content as is in
1120
+ * plain and `githubLog`.
1121
+ *
1122
+ * @param content - any number of strings, inlines or arrays of them, in order
1123
+ */
1124
+ static em(...content: Array<InlineInput>): InlineOf<"Emphasis">;
1125
+ /**
1126
+ * A file path, shown through the context's `displayPath` and never linked.
1127
+ *
1128
+ * @param path - the path, usually absolute
1129
+ */
1130
+ static file(path: string): InlineOf<"File">;
1131
+ /**
1132
+ * A path or breadcrumb; a renderer joins the segments with the audience's separator.
1133
+ *
1134
+ * @param segments - the segments, in order
1135
+ */
1136
+ static path(...segments: Array<string>): InlineOf<"Path">;
1137
+ /**
1138
+ * A heading.
1139
+ *
1140
+ * @param level - 1 to 4
1141
+ * @param content - the heading text
1142
+ */
1143
+ static heading(level: 1 | 2 | 3 | 4, content: InlineInput): BlockOf<"Heading">;
1144
+ /**
1145
+ * One logical line of content.
1146
+ *
1147
+ * @param content - any number of strings, inlines or arrays of them, in order
1148
+ */
1149
+ static paragraph(...content: Array<InlineInput>): BlockOf<"Paragraph">;
1150
+ /**
1151
+ * A list of blocks.
1152
+ *
1153
+ * @param items - the items
1154
+ * @param options - `cap`, `overflow`, and `compact` for no blank lines inside an item
1155
+ */
1156
+ static list(items: ReadonlyArray<Block>, options?: ListOptions): BlockOf<"List">;
1157
+ /**
1158
+ * A table.
1159
+ *
1160
+ * @param columns - the columns: a header and an optional alignment each
1161
+ * @param rows - the rows; each cell takes a string, an inline or an array of either
1162
+ * @param options - `cap`, `overflow`, and `style: "pipe"` for istanbul's shape in plain and `ansi`
1163
+ */
1164
+ static table(columns: ReadonlyArray<{
1165
+ readonly header: InlineInput;
1166
+ readonly align?: "left" | "right" | "center";
1167
+ }>, rows: ReadonlyArray<ReadonlyArray<InlineInput>>, options?: TableOptions): BlockOf<"Table">;
1168
+ /**
1169
+ * A tree of labels.
1170
+ *
1171
+ * @param root - the root; a node's `children` may be left out
1172
+ */
1173
+ static tree(root: TreeInput): BlockOf<"Tree">;
1174
+ /**
1175
+ * A titled body a renderer may fold.
1176
+ *
1177
+ * @param title - the title
1178
+ * @param body - the body
1179
+ * @param options - `open` asks for it to start unfolded
1180
+ */
1181
+ static collapsible(title: InlineInput, body: ReadonlyArray<Block>, options?: {
1182
+ readonly open?: boolean;
1183
+ }): BlockOf<"Collapsible">;
1184
+ /**
1185
+ * A callout.
1186
+ *
1187
+ * @param kind - `note`, `tip`, `important`, `warning` or `caution`
1188
+ * @param body - the body
1189
+ */
1190
+ static callout(kind: "note" | "tip" | "important" | "warning" | "caution", body: ReadonlyArray<Block>): BlockOf<"Callout">;
1191
+ /**
1192
+ * Preformatted text.
1193
+ *
1194
+ * @param text - the text
1195
+ * @param lang - its language, for a renderer that fences it
1196
+ */
1197
+ static codeBlock(text: string, lang?: string): BlockOf<"CodeBlock">;
1198
+ /**
1199
+ * Expected against received text.
1200
+ *
1201
+ * @param expected - the expected text
1202
+ * @param received - the received text
1203
+ * @param options - `cap` limits the lines shown
1204
+ */
1205
+ static diff(expected: string, received: string, options?: {
1206
+ readonly cap?: number;
1207
+ }): BlockOf<"Diff">;
1208
+ /**
1209
+ * Children under an optional title.
1210
+ *
1211
+ * @param title - the title, or `undefined` for none
1212
+ * @param children - the blocks
1213
+ */
1214
+ static section(title: InlineInput | undefined, children: ReadonlyArray<Block>): BlockOf<"Section">;
1215
+ /**
1216
+ * One counter of a `Counts` block, with its status definition resolved.
1217
+ *
1218
+ * @remarks
1219
+ * A name the vocabulary does not have is a compile error.
1220
+ *
1221
+ * @param vocab - the vocabulary the status belongs to
1222
+ * @param name - a status name in it
1223
+ * @param options - the counter's `key`, `label` and count `n`, and `showZero` to keep it when `n` is zero
1224
+ */
1225
+ static counter<N extends string>(vocab: Status<N>, name: NoInfer<N>, options: {
1226
+ readonly key: string;
1227
+ readonly label: string;
1228
+ readonly n: number;
1229
+ readonly showZero?: boolean;
1230
+ }): Counter;
1231
+ /**
1232
+ * Counters in one of three layouts.
1233
+ *
1234
+ * @param options - the counters, the layout and the optional label, total rule, qualifier and duration
1235
+ */
1236
+ static counts(options: CountsOptions): BlockOf<"Counts">;
1237
+ /**
1238
+ * Counters as a table: a row per entry, a column per counter key (in the order the keys first appear, headed by
1239
+ * the counter's label), and an optional total row summing each column.
1240
+ *
1241
+ * @remarks
1242
+ * A row without a counter for some key leaves that cell empty, and it counts as zero in the total. A counter whose
1243
+ * `n` is zero shows `0`, as a `Doc.table` cell would: a counter's `showZero` has no effect in a table, only in a
1244
+ * `Counts` block, so there is no need to set it. `totalRow`
1245
+ * labels the total row with a plain `Total` when `true`, or with the content given: for a bold one, pass
1246
+ * `totalRow: Doc.strong("Total")`. A column is headed by its counter's `label`; a counter's status paints its cells
1247
+ * in `ansi` and is ignored in markdown, so a plain numbers table may pass any status. `labelHeader` heads the label column, which
1248
+ * is otherwise empty. When some row has a `durationMs`, a last column shows it with `Fmt.duration`, headed
1249
+ * `durationHeader` (`duration` by default); a row without one has an empty cell there and counts as zero in the
1250
+ * total row's summed duration.
1251
+ *
1252
+ * @param rows - each row's label, counters and optional duration
1253
+ * @param options - `totalRow`, to add the summed row; the label and duration column headers
1254
+ */
1255
+ static countsTable(rows: ReadonlyArray<{
1256
+ readonly label: InlineInput;
1257
+ readonly counters: ReadonlyArray<Counter>;
1258
+ readonly durationMs?: number;
1259
+ }>, options?: CountsTableOptions): BlockOf<"CountsTable">;
1260
+ /**
1261
+ * Lines, one per entry, in every renderer: markdown joins them with hard breaks so they never collapse into one.
1262
+ *
1263
+ * @param lines - the entries; each takes a string, an inline or an array of either
1264
+ */
1265
+ static lines(lines: ReadonlyArray<InlineInput>): BlockOf<"Lines">;
1266
+ /**
1267
+ * One line of content; with `truncate`, it is cut to the width with the glyph set's ellipsis instead of wrapping.
1268
+ *
1269
+ * @remarks
1270
+ * Without `truncate` a line longer than the width wraps. For a single line that must never wrap nor be cut, such
1271
+ * as a test's full name used as a title, use {@link Doc.verbatim}.
1272
+ *
1273
+ * @param content - the line
1274
+ * @param options - `truncate`
1275
+ */
1276
+ static line(content: InlineInput, options?: {
1277
+ readonly truncate?: boolean;
1278
+ }): BlockOf<"Line">;
1279
+ /**
1280
+ * A unified diff as given, such as a test runner's: sanitized, its `+` and `-` lines painted `success` and
1281
+ * `failure` in `ansi`, and a `diff` fence in markdown.
1282
+ *
1283
+ * @remarks
1284
+ * With `truncate`, plain and `ansi` cut each line to the width with the glyph set's ellipsis instead of wrapping
1285
+ * it; an agent's or a CI's width is unbounded, so nothing is cut for them unless the context gives a finite width.
1286
+ * Markdown keeps every line whole. Inside a compact list item a blank line of the diff keeps the item's indent.
1287
+ *
1288
+ * A trailing line break ends the last line, as in a unified diff file, and adds no blank line after it: `"a\n"` is
1289
+ * one line. To end on a blank line, end the text with two line breaks.
1290
+ *
1291
+ * @param unified - the diff
1292
+ * @param options - `cap`, the most lines shown; `truncate`, to cut each line to the width
1293
+ */
1294
+ static diffText(unified: string, options?: {
1295
+ readonly cap?: number;
1296
+ readonly truncate?: boolean;
1297
+ }): BlockOf<"DiffText">;
1298
+ /**
1299
+ * Lines kept exactly: each indented by `indent` spaces, sanitized, and never wrapped.
1300
+ *
1301
+ * @remarks
1302
+ * Plain, `ansi` and `githubLog` write the lines as they are; markdown fences them, so the indentation survives.
1303
+ *
1304
+ * It is the tool for a single line that must never wrap nor be cut, whatever the width: {@link Doc.line} wraps at
1305
+ * the width, or cuts with `truncate`, and `verbatim` does neither.
1306
+ *
1307
+ * @param text - the lines
1308
+ * @param options - `indent`, the spaces in front of every line; none by default
1309
+ */
1310
+ static verbatim(text: string, options?: {
1311
+ readonly indent?: number;
1312
+ }): BlockOf<"Verbatim">;
1313
+ /**
1314
+ * A GitHub Actions annotation: `Render.githubLog` writes it as one workflow command (`::error file=…::message`),
1315
+ * and every other renderer writes nothing.
1316
+ *
1317
+ * @remarks
1318
+ * It is the kit's own command, so `githubLog` does not neutralize it; its message and properties are escaped, so
1319
+ * no text in them can end the command or start another. It is a command where a line starts: at the top level, as a
1320
+ * top-level section's child, or as a direct child of a group's body. Nested deeper, it is dropped.
1321
+ *
1322
+ * @param options - the level, and the optional file, position and title
1323
+ * @param message - what it says
1324
+ */
1325
+ static annotation(options: AnnotationOptions, message: string): BlockOf<"Annotation">;
1326
+ /**
1327
+ * The total of a `Counts` block: the caller's rule when it has one, otherwise the sum of `n` over every counter.
1328
+ *
1329
+ * @remarks
1330
+ * The rule sees every counter, including the ones a renderer hides, so hiding never changes the total.
1331
+ *
1332
+ * @param block - the `Counts` block
1333
+ */
1334
+ static total(block: BlockOf<"Counts">): number;
1335
+ /**
1336
+ * The counters a renderer shows: every one except a zero counter that does not ask for `showZero`.
1337
+ *
1338
+ * @param block - the `Counts` block
1339
+ */
1340
+ static visibleCounters(block: BlockOf<"Counts">): ReadonlyArray<Counter>;
1341
+ /**
1342
+ * Render a document for whoever is running the program and write it to a stream.
1343
+ *
1344
+ * @remarks
1345
+ * The context is {@link Render.context} for the stream, so the width, the colour, the links and the audience
1346
+ * come from the services the program already has, and the text is written with `Console.log` or
1347
+ * `Console.error`: a test captures it by swapping the `Console`. With `format: "auto"` the renderer follows
1348
+ * the audience, and the width is unbounded for an agent and a CI.
1349
+ *
1350
+ * An agent is never written an escape of any kind, even with an explicit `format: "ansi"`: its context is
1351
+ * colourless and its links are off. A document that renders to nothing prints nothing.
1352
+ *
1353
+ * `CurrentRuntimeEnv` is read if the environment has one and is not required: a `ci` audience prints
1354
+ * GitHub's log format only when it says GitHub Actions, and plain text otherwise, including when it is
1355
+ * absent. An explicit `format` is honoured whatever the audience.
1356
+ *
1357
+ * @param doc - the document
1358
+ * @param options - the stream and the format
1359
+ */
1360
+ static readonly print: (doc: Document, options?: DocPrintOptions) => Effect.Effect<void, never, CliTheme | TerminalEnv | Audience | CliLinks>;
1361
+ }
1362
+ //#endregion
1363
+ //#region src/CliLinks.d.ts
1364
+ /**
1365
+ * Whether file links open in an editor.
1366
+ *
1367
+ * @remarks
1368
+ * `vscode` writes `vscode://file/<path>:<line>:<col>`, `file` writes `file://<path>`, `off` writes no file link, and
1369
+ * `auto` picks `vscode` when it finds a signal of VS Code and `file` otherwise.
1370
+ *
1371
+ * @public
1372
+ */
1373
+ type EditorLinks = "auto" | "vscode" | "file" | "off";
1374
+ /**
1375
+ * The shape of the {@link CliLinks} service: the mode decided, and the URL a link target becomes.
1376
+ *
1377
+ * @public
1378
+ */
1379
+ interface CliLinksShape {
1380
+ /** The mode `auto` resolved to, or the one that was asked for. */
1381
+ readonly mode: "vscode" | "file" | "off";
1382
+ /**
1383
+ * The URL a target opens, or `None` when it has none.
1384
+ *
1385
+ * @remarks
1386
+ * A `{ url }` target is its URL, whatever the mode. A `{ file }` target is `vscode://file/<path>:<line>:<col>` or
1387
+ * `file://<path>`, with the path URL-encoded; `off` gives it none, and so does a relative path when the layer
1388
+ * has no working directory to resolve it against. A column needs a line.
1389
+ */
1390
+ readonly target: (target: LinkTarget) => Option.Option<string>;
1391
+ }
1392
+ /**
1393
+ * Options for {@link CliLinks.layer}.
1394
+ *
1395
+ * @public
1396
+ */
1397
+ interface CliLinksOptions {
1398
+ /** The setting, `auto` by default. An environment variable the consumer names beats it. */
1399
+ readonly editorLinks?: EditorLinks | undefined;
1400
+ /** The environment variable that overrides the setting, read through `Config`. Not read unless named. */
1401
+ readonly envVar?: string | undefined;
1402
+ /** The working directory; `PWD` through `Config`, then `Path.resolve(".")`, when omitted. */
1403
+ readonly cwd?: string | undefined;
1404
+ }
1405
+ /**
1406
+ * The options of {@link CliLinks.linker}.
1407
+ *
1408
+ * @public
1409
+ */
1410
+ interface CliLinksLinkerOptions {
1411
+ /** The service that turns a target into a URL. */
1412
+ readonly links: CliLinksShape;
1413
+ /** Whether the stream's terminal renders OSC 8 hyperlinks. */
1414
+ readonly hyperlinks: boolean;
1415
+ /** Who the output is for. */
1416
+ readonly audience: AudienceKind;
1417
+ }
1418
+ declare const CliLinks_base: Context.ServiceClass<CliLinks, "@effected/cli/CliLinks", CliLinksShape>;
1419
+ /**
1420
+ * Editor-aware links for file targets: where a link to a file opens.
1421
+ *
1422
+ * @remarks
1423
+ * The mode is decided once, when the layer is built. `auto` is `vscode` when `CurrentRuntimeEnv.terminal` is
1424
+ * `vscode` (`TERM_PROGRAM=vscode`) or a `.vscode/` directory sits at the project root, and `file` otherwise. The
1425
+ * root is the nearest directory, from the working directory up, that holds `.git` or `pnpm-workspace.yaml`; the
1426
+ * climb is bounded, at most 64 directories above the working directory, and stops where `Path.dirname` reaches the
1427
+ * filesystem root. With no root, the working directory itself is checked.
1428
+ *
1429
+ * Whether a link is written at all is a separate question, answered by {@link CliLinks.linker}.
1430
+ *
1431
+ * @public
1432
+ */
1433
+ export declare class CliLinks extends CliLinks_base {
1434
+ /**
1435
+ * The links for the working directory, reading the filesystem for a `.vscode/` directory.
1436
+ *
1437
+ * @remarks
1438
+ * A layer-returning function mints a fresh layer per call: call it once and bind the result to a constant.
1439
+ *
1440
+ * @param options - the setting, the environment variable that overrides it, and the working directory
1441
+ */
1442
+ static readonly layer: (options?: CliLinksOptions) => Layer.Layer<CliLinks, never, FileSystem.FileSystem | Path.Path | CurrentRuntimeEnv>;
1443
+ /**
1444
+ * Links fixed to a mode, with no filesystem: a relative path has no link, since there is no working directory.
1445
+ *
1446
+ * @param mode - `vscode`, `file` or `off`
1447
+ */
1448
+ static readonly layerTest: (mode: "vscode" | "file" | "off") => Layer.Layer<CliLinks>;
1449
+ /**
1450
+ * The function that writes a link: a target and a label in, the label out, wrapped in OSC 8 when it should be.
1451
+ *
1452
+ * @remarks
1453
+ * It writes the hyperlink `ESC ] 8 ; ; URL ESC \ label ESC ] 8 ; ; ESC \` only when the stream's terminal can
1454
+ * render it (`hyperlinks`) and the audience is not an agent, which never gets an escape of any kind; in every other
1455
+ * case, and whenever the target has no URL, it returns the label unchanged. The URL has its control characters
1456
+ * removed again here, so a hostile target cannot end the sequence early or start another, and a URL whose scheme is
1457
+ * not one a link may have (`javascript:`, `data:`, and the like; the same list markdown uses) is the label alone. It is pure and cheap,
1458
+ * which {@link RenderContext}'s `link` requires.
1459
+ *
1460
+ * @param options - the links, whether hyperlinks are available, and the audience
1461
+ */
1462
+ static readonly linker: (options: CliLinksLinkerOptions) => (target: LinkTarget, label: string) => string;
1463
+ }
1464
+ //#endregion
1465
+ //#region src/CliLogger.d.ts
1466
+ /**
1467
+ * How a log record is turned into a line.
1468
+ *
1469
+ * @public
1470
+ */
1471
+ interface CliLoggerOptions {
1472
+ /**
1473
+ * Render one message. Defaults to joining an array with spaces and
1474
+ * `String`-ing anything else.
1475
+ *
1476
+ * @remarks
1477
+ * An array arrives because `Effect.log("synced", 3, "repos")` is variadic.
1478
+ *
1479
+ * The text is sanitised, because it is whatever the program logged: with the default render, the line has its
1480
+ * escape sequences and control characters removed (a line break stays one, a tab becomes a space); with yours, you
1481
+ * receive the string parts already sanitised and own what you add, a colour included. Under GitHub Actions, where
1482
+ * `CurrentRuntimeEnv` says so, a line the runner would read as a workflow command is neutralized either way.
1483
+ *
1484
+ * The logger reads `CurrentRuntimeEnv` from the logging fiber's context, so a line logged outside its scope
1485
+ * (`CliLogger.layer()` provided alone, with no `CurrentRuntimeEnv`, or the warnings logged while `CliRuntime.main`
1486
+ * builds its environment) is sanitised but not neutralized. Provide the environment around the program, as `main`
1487
+ * does, for the neutralizing to apply.
1488
+ */
1489
+ readonly render?: ((message: unknown) => string) | undefined;
1490
+ /**
1491
+ * The level at and above which output goes to stderr. Defaults to `"All"`,
1492
+ * so every log level is a diagnostic and stdout carries only what the
1493
+ * program writes with `Console.log`.
1494
+ *
1495
+ * @remarks
1496
+ * Pass `"Error"` to send only errors to stderr, for a tool whose output *is*
1497
+ * its log lines rather than a separate document written with
1498
+ * `Console.log`.
1499
+ */
1500
+ readonly stderrFrom?: LogLevel.LogLevel | undefined;
1501
+ }
1502
+ /**
1503
+ * A `Logger` that renders CLI output rather than service logs: no timestamp, level or fiber id, with every level
1504
+ * going to stderr by default so stdout carries only what the program writes.
1505
+ *
1506
+ * @remarks
1507
+ * Effect's default logger emits `[00:33:56.619] INFO (#2): message`. That is
1508
+ * the right shape for a long-running service being scraped and the wrong one
1509
+ * for a tool a person is watching: the timestamp, level and fiber id are noise
1510
+ * in front of output a human is reading, and they make a formatted block — a
1511
+ * permissions table, a summary — unreadable.
1512
+ *
1513
+ * **A program that never installs a CLI logger looks correct in review and
1514
+ * ships timestamps to its users**, so install this one at the program's
1515
+ * boundary.
1516
+ *
1517
+ * It writes through the `Console` reference read off the logging fiber,
1518
+ * synchronously, as core's own default logger does: `Logger.make` takes a
1519
+ * synchronous callback, and a `Stdio` sink write is an `Effect` a logger cannot
1520
+ * `yield*`. `Console.Console` is a `Context.Reference`, so it never appears in
1521
+ * `R`, and a test swaps the reference rather than stubbing a global.
1522
+ *
1523
+ * @example
1524
+ * ```ts
1525
+ * import { CliLogger } from "@effected/cli"
1526
+ * import { Console, Effect } from "effect"
1527
+ *
1528
+ * const program = Effect.gen(function* () {
1529
+ * yield* Effect.log("synced 3 repos") // stderr, no timestamp — a diagnostic
1530
+ * yield* Console.log("3 repos synced") // stdout — the program's actual output
1531
+ * yield* Effect.logError("one failed") // stderr
1532
+ * })
1533
+ *
1534
+ * program.pipe(Effect.provide(CliLogger.layer()))
1535
+ * ```
1536
+ *
1537
+ * `stderrFrom` defaults to `"All"`: a CLI's stdout is its product, so every
1538
+ * log level is a diagnostic unless a consumer narrows the threshold. Write
1539
+ * program output with `Console.log`, never `Effect.log`. Pass
1540
+ * `stderrFrom: "Error"` for a tool whose output *is* its log lines.
1541
+ *
1542
+ * @public
1543
+ */
1544
+ export declare class CliLogger {
1545
+ private constructor();
1546
+ /**
1547
+ * The logger itself, for composing into an existing `Logger.layer` set.
1548
+ *
1549
+ * @remarks
1550
+ * Prefer {@link CliLogger.layer}. Reach for this only when you are building
1551
+ * the logger set yourself and want this one among several.
1552
+ */
1553
+ static readonly make: (options?: CliLoggerOptions) => Logger.Logger<unknown, void>;
1554
+ /**
1555
+ * Replace the default logger with this one.
1556
+ *
1557
+ * @remarks
1558
+ * `Logger.layer` **replaces** rather than merges, so nothing is emitted twice.
1559
+ *
1560
+ * Merge this into the layer you provide to the whole program rather than
1561
+ * providing it beneath: merged, it also covers lines emitted during layer
1562
+ * construction, which is exactly where a startup failure prints.
1563
+ */
1564
+ static readonly layer: (options?: CliLoggerOptions) => Layer.Layer<never>;
1565
+ }
1566
+ //#endregion
1567
+ //#region src/CliLog.d.ts
1568
+ /**
1569
+ * Options for `CliLog.layer`.
1570
+ *
1571
+ * @public
1572
+ */
1573
+ interface CliLogOptions {
1574
+ /**
1575
+ * The diagnostics level, for a host that has already decided it.
1576
+ *
1577
+ * @remarks
1578
+ * Beats `envVar`, which is then not read at all (so a bad value there neither warns nor overrides this), and
1579
+ * loses to core's `--log-level` flag like every diagnostics level: the sink follows the flag while it is set.
1580
+ */
1581
+ readonly level?: LogLevel.LogLevel | undefined;
1582
+ /**
1583
+ * The environment variable that sets the diagnostics level, for example `MYTOOL_LOG_LEVEL`. Read through
1584
+ * `Config`, never `process`. Unset or empty means `None`. Ignored when `level` is given.
1585
+ */
1586
+ readonly envVar?: string | undefined;
1587
+ /**
1588
+ * Whether to install the plain `CliLogger` for ordinary lines. `true` by default.
1589
+ *
1590
+ * @remarks
1591
+ * `false` is the diagnostics-only mode for a library host (an MCP server, a test reporter): the layer installs
1592
+ * only the diagnostics sink, the file sink if any, and `extraLoggers`. With no level set as well, stderr gets
1593
+ * no output. An invalid level in `envVar` still prints its one warning line, through a private `CliLogger`,
1594
+ * since that is a configuration error the host should see.
1595
+ *
1596
+ * Only `CliLog`'s own records are silenced. Under `CliRuntime.main`, what the platform logs while it builds follows
1597
+ * the build-time format: in NDJSON (`json`, or `auto` for an agent or a CI) it goes through this layer, so `false`
1598
+ * silences it there as at runtime; otherwise it goes through a plain `CliLogger`, routed by `logger.stderrFrom` as the
1599
+ * host set it. The audience-override warning is a
1600
+ * configuration error and is never silenced: it is written exactly once, as NDJSON when the build-time format is
1601
+ * NDJSON and as a plain line otherwise, whatever this option says, to stderr alone (never stdout, whatever
1602
+ * `logger.stderrFrom` says) and never to `extraLoggers` or the file sink. The failure report and the `CliMessage` lines
1603
+ * always go through a plain `CliLogger`.
1604
+ */
1605
+ readonly plainLogger?: boolean | undefined;
1606
+ /**
1607
+ * `json` is NDJSON, `pretty` a human line, `auto` (the default) decides by the audience alone: NDJSON for an agent
1608
+ * or a CI, the pretty `CliLogger` line for a human, at build time and at runtime alike.
1609
+ *
1610
+ * @remarks
1611
+ * Stderr's terminal state is never consulted, so a human piping stderr to a file gets plain lines, never a mix of
1612
+ * plain and NDJSON; colour still follows `stderr.color`, so those lines carry no escapes when stderr is not a colour
1613
+ * terminal. Pass `format: "json"` for machine-readable logs whoever runs the program.
1614
+ *
1615
+ * Under `CliRuntime.main` with `env.log`, what the platform logs while it builds is written before the platform
1616
+ * provides the terminal or the arguments, so `auto` decides those lines from what needs no platform: an audience
1617
+ * flag in {@link CliLogOptions.argv}, else the audience override variable (`env.audienceEnvVar`), else agent and
1618
+ * CI detection from the environment. An agent or a CI gets NDJSON, as its runtime lines are; anything else gets
1619
+ * a plain line, the same choice the runtime lines make. `json` is NDJSON and `pretty` plain throughout.
1620
+ */
1621
+ readonly format?: "auto" | "json" | "pretty" | undefined;
1622
+ /**
1623
+ * The program's arguments, for the format of what the platform logs while it builds under `CliRuntime.main` with
1624
+ * `format: "auto"`: an audience flag (`--agent`, `--ci`, `--human`, `--audience <kind>`) in them decides those
1625
+ * lines as it decides the run's. Only `CliRuntime.main` reads this.
1626
+ *
1627
+ * @remarks
1628
+ * The arguments core parses come from the platform's `Stdio`, which does not exist until the platform is built, and
1629
+ * this package never reads `process`. A Node host passes `process.argv.slice(2)`; without it the build-time lines
1630
+ * follow the environment alone, so `--agent` with no agent detected gets plain lines until the platform is built.
1631
+ */
1632
+ readonly argv?: ReadonlyArray<string> | undefined;
1633
+ /** Options for the `CliLogger` this layer builds for ordinary log lines; see {@link CliLoggerOptions}. */
1634
+ readonly logger?: CliLoggerOptions | undefined;
1635
+ /**
1636
+ * Loggers to keep beside the `CliLogger` and the sink, for example a telemetry logger: the layer owns the
1637
+ * whole set, so anything not listed here is dropped.
1638
+ *
1639
+ * @remarks
1640
+ * Each is floored at the `MinimumLogLevel` it had, like the `CliLogger`, so lowering the level for the
1641
+ * diagnostics sink never makes it see records it would not have seen otherwise.
1642
+ */
1643
+ readonly extraLoggers?: ReadonlyArray<Logger.Logger<unknown, unknown>> | undefined;
1644
+ /**
1645
+ * Whether a line the GitHub Actions runner would read as a workflow command is neutralized. `auto`, the default,
1646
+ * follows the `CurrentRuntimeEnv` of the fiber that logs, and where that fiber has none, `runtimeEnv`, else the one
1647
+ * the layer was built with: a host that builds this layer over its environment covers records logged outside it
1648
+ * too. `true` always neutralizes, `false` never does.
1649
+ */
1650
+ readonly neutralize?: boolean | "auto" | undefined;
1651
+ /**
1652
+ * The runtime environment `neutralize: "auto"` falls back to for a record whose fiber has no `CurrentRuntimeEnv`.
1653
+ *
1654
+ * @remarks
1655
+ * Given, it is used in place of the `CurrentRuntimeEnv` the layer captured when it was built, and beats it; the
1656
+ * logging fiber's own `CurrentRuntimeEnv` still comes first. The capture is invisible in the layer's type (it is read
1657
+ * if present, never required), so a host that builds this layer outside its environment either passes the snapshot
1658
+ * here, for example `RuntimeEnv.fromRecord(process.env)`, or provides `CurrentRuntimeEnv` around the layer.
1659
+ *
1660
+ * Neutralization under `"auto"` reads the logging fiber's `CurrentRuntimeEnv`, then this option, then the
1661
+ * `CurrentRuntimeEnv` captured when the layer was built. With none of the three, a record is not neutralized: set
1662
+ * this option, provide `CurrentRuntimeEnv` around the layer, or pass `neutralize: true`. Every `CliLog.layer`
1663
+ * overload points here.
1664
+ */
1665
+ readonly runtimeEnv?: RuntimeEnv | undefined;
1666
+ }
1667
+ /**
1668
+ * Where the file sink writes: a literal path, or the environment variable that holds it.
1669
+ *
1670
+ * @public
1671
+ */
1672
+ type CliLogFile = {
1673
+ readonly envVar: string;
1674
+ } | {
1675
+ readonly path: string;
1676
+ };
1677
+ /**
1678
+ * {@link CliLogOptions} with a file sink, which is what makes the layer require `FileSystem` and `Path`.
1679
+ *
1680
+ * @public
1681
+ */
1682
+ interface CliLogFileOptions extends CliLogOptions {
1683
+ /**
1684
+ * Also write every record the sink accepts to a file as NDJSON, asynchronously.
1685
+ *
1686
+ * @remarks
1687
+ * The line is identical to the stderr NDJSON line for the same log call and is filtered by the same
1688
+ * {@link CliLog.Level}. Lines are queued and appended by a fiber scoped to the layer, so logging never waits
1689
+ * on the disk, and the parent directory is created. The first write error prints exactly one stderr line,
1690
+ * `diagnostics log file <path> failed: <message>; further file logging disabled`, and the program keeps
1691
+ * running with its normal exit code: from then on lines, including any still queued, are discarded silently.
1692
+ * Closing the layer's scope flushes the lines queued before it, unless the sink had already disabled itself.
1693
+ * `{ envVar }` names the variable that holds the path; unset or empty, no file is written.
1694
+ *
1695
+ * `undefined` writes no file but keeps the requirements of a file sink (`FileSystem` and `Path`) in `R`, so a
1696
+ * host whose sink is optional has one stable layer type either way.
1697
+ *
1698
+ * Under `CliRuntime.main` with `env.log`, what the platform logs while it builds, before it provides
1699
+ * `FileSystem`, reaches stderr but not the file; the file starts with the program's own records.
1700
+ */
1701
+ readonly file: CliLogFile | undefined;
1702
+ }
1703
+ /**
1704
+ * Diagnostics kept apart from a program's output: a level, a format and a place to write.
8
1705
  *
9
1706
  * @remarks
10
- * Follows the no-color.org rule: colour is off when stdout is not a
11
- * terminal, or when `NO_COLOR` is set to any **non-empty** value — an empty
12
- * `NO_COLOR=""` does not disable colour. `FORCE_COLOR` is ignored, matching
13
- * core's own formatter. The environment is read through the ambient
14
- * `ConfigProvider`, never `process`, so a test swaps it with
15
- * `Effect.provideService(ConfigProvider.ConfigProvider, ...)`. The kit's
16
- * default providers (`fromEnv`, `fromUnknown`) already treat an empty
17
- * `NO_COLOR` as unset, so the explicit `set === ""` check exists for a
18
- * provider constructed with `{ preserveEmptyStrings: true }`.
1707
+ * Two kinds of line must never be silenced by a diagnostics default: the failure report
1708
+ * `CliRuntime.reportFailures` writes through `CliLogger`, and the `CliMessage` lines. This package therefore
1709
+ * never makes the diagnostics level a global switch. The diagnostics logger filters on its **own** threshold,
1710
+ * {@link CliLog.Level}, and writes to stderr only.
1711
+ *
1712
+ * `CliLog.layer` **owns the whole logger set**. It builds a `CliLogger` for ordinary log lines and the
1713
+ * diagnostics sink itself and replaces whatever was installed, without reading it, so there is no order to get
1714
+ * wrong. Use it instead of `CliLogger.layer`, not with it: `CliLogger.layer` alone is the no-diagnostics path,
1715
+ * and a `CliLogger.layer` layered on top would replace this layer's set.
1716
+ *
1717
+ * Effect drops a record below `MinimumLogLevel` before any logger runs, so to let a debug record reach the
1718
+ * diagnostics logger `MinimumLogLevel` has to be lowered. `CliLog.layer` does that only when the
1719
+ * diagnostics level is below the ambient minimum, and in the same step floors the `CliLogger` it built at the
1720
+ * minimum it had, so it never prints a record the diagnostics level alone let through. A failure report is
1721
+ * written outside the scope core's `--log-level` flag sets, so `--log-level none` does not silence it either.
1722
+ *
1723
+ * `format: "auto"`, the default, decides by the audience alone, at build time and at runtime alike: NDJSON for an
1724
+ * agent or a CI, the pretty line for a human, whatever stderr's terminal state. A human piping stderr therefore gets
1725
+ * plain lines (without escapes unless stderr is a colour terminal); pass `format: "json"` for machine-readable logs.
1726
+ *
1727
+ * The text a program logs is sanitised before anything is painted in the pretty line (the message, the component and an
1728
+ * error's cause lose their escape sequences and control characters), and under GitHub Actions, where
1729
+ * `CurrentRuntimeEnv` in the logging fiber's context says so, a line the runner would read as a workflow command is
1730
+ * neutralized. An NDJSON record is not safe merely because `JSON.stringify` escapes control characters: the runner's
1731
+ * legacy parser reads `##[` anywhere in a line, so under Actions it is written as the JSON escape `#\u0023[`, which
1732
+ * decodes to the identical text. The file sink's lines are not read by the runner and are written as they are.
1733
+ *
1734
+ * `CurrentRuntimeEnv` is read from the logging fiber's context, and where that has none, from the `runtimeEnv` option,
1735
+ * else from the layer's own build context (captured if present, never required), so a host that builds the layer over
1736
+ * its environment, or names it with `runtimeEnv`, neutralizes every record. A record with none of the three (a program
1737
+ * with no `CurrentRuntimeEnv` anywhere) is sanitised but not neutralized, unless the `neutralize` option says otherwise.
1738
+ *
1739
+ * Core's `--log-level` flag sets `MinimumLogLevel` inside the command. While it is set to something other than
1740
+ * the value this layer installed, the diagnostics logger follows the flag instead of its own level: it writes
1741
+ * every record that reaches it. The `CliLogger` prints the same record too, so a record at or above the ambient
1742
+ * minimum is written twice, once plain and once to the diagnostics sink. Stderr is therefore not pure NDJSON while
1743
+ * diagnostics are on: a parser reads the lines that start with `{`.
1744
+ *
1745
+ * One edge: a `--log-level` value that EQUALS the level this layer installed cannot be told from no flag, so the
1746
+ * sink keeps filtering on its own level rather than following the flag. The plain `CliLogger` prints those records
1747
+ * regardless.
1748
+ *
1749
+ * @example
1750
+ * ```ts
1751
+ * import { CliRuntime } from "@effected/cli"
1752
+ * import { NodeRuntime, NodeServices } from "@effect/platform-node"
1753
+ *
1754
+ * // `env.log` makes `main` install `CliLog.layer`: set MYTOOL_LOG_LEVEL=debug to get diagnostics on stderr.
1755
+ * NodeRuntime.runMain(
1756
+ * CliRuntime.main(program, {
1757
+ * platform: NodeServices.layer,
1758
+ * env: { audienceEnvVar: "MYTOOL_AUDIENCE", log: { envVar: "MYTOOL_LOG_LEVEL" } },
1759
+ * }),
1760
+ * )
1761
+ * ```
19
1762
  *
20
1763
  * @public
21
1764
  */
22
- export declare class CliColor {
1765
+ export declare class CliLog {
23
1766
  private constructor();
24
1767
  /**
25
- * The decision, for passing to a pure renderer as a plain boolean.
1768
+ * The diagnostics threshold. Defaults to `None`, silent.
26
1769
  *
27
- * @public
1770
+ * @remarks
1771
+ * `CliLog.layer` sets it from the environment variable. A scope may raise it to narrow the output; it
1772
+ * cannot lower it below the level the layer installed, because Effect has already dropped those records.
28
1773
  */
29
- static readonly enabled: Effect.Effect<boolean, never, Stdio.Stdio>;
1774
+ static readonly Level: Context.Reference<LogLevel.LogLevel>;
30
1775
  /**
31
- * Core's default `CliOutput.Formatter`, coloured by the same decision as
32
- * {@link CliColor.enabled}, so help text, parse errors and rendered
33
- * output never disagree on whether colour is on.
1776
+ * The whole logger set of a program: a `CliLogger` for ordinary lines plus the diagnostics sink.
34
1777
  *
35
1778
  * @remarks
36
- * `overrides` replaces individual methods of the default formatter — for
37
- * example `formatVersion`, to append a "via `<carrier>`" line without
38
- * losing the other defaults. Each call mints a fresh layer; bind the
39
- * result to a `const` or the decision is re-read every time it is
40
- * provided.
1779
+ * Replaces the installed loggers without reading them. Provide it on the program you pass to
1780
+ * `CliRuntime.main`, or use `main`'s `env.log` option; never wrap it around `main`, whose own logger would
1781
+ * replace this one. Bind it to a constant.
41
1782
  *
42
- * The `never` in its output does not mean it installs nothing.
43
- * `CliOutput.Formatter` is a `Context.Reference`, whose key type is
44
- * `never`, so this layer sets the formatter reference rather than
45
- * providing a service. Every command it is provided to renders with the
46
- * formatter it sets, and without it they fall back to core's default
47
- * formatter.
1783
+ * The requirements follow the format. `format: "json"` reads neither the audience nor the terminal, so it
1784
+ * requires neither; `"pretty"` requires `TerminalEnv` alone, for the stderr colour; `"auto"` and an omitted
1785
+ * format require both. A long-lived host that never builds a platform `Terminal` can provide
1786
+ * `TerminalEnv.layerStdio()` for the pretty case.
48
1787
  *
49
- * @public
1788
+ * A platform or program that installs its own `Logger.layer([...])` replaces this set: do not. The diagnostics
1789
+ * then go silent with no error.
1790
+ *
1791
+ * The NDJSON line is core's `Logger.formatJson`, unchanged so it stays interoperable with Effect tooling: the
1792
+ * `message` field is a string for one log argument and an array for several.
1793
+ *
1794
+ * Level parsing is case-insensitive and accepts `warn`, `warning`, `error`, `info`, `debug`, `trace`,
1795
+ * `fatal`, `all` and `none`. An invalid value warns once, through the `CliLogger`, and leaves diagnostics
1796
+ * off.
1797
+ *
1798
+ * With a `file` option ({@link CliLogFileOptions}) the layer also writes an async NDJSON file, and only then
1799
+ * does it require `FileSystem` and `Path`, and it leaves them in `R` unprovided: the platform supplies them, or
1800
+ * a test supplies a memory filesystem, so a host never provides Node inside its own layer.
1801
+ *
1802
+ * Without a `CurrentRuntimeEnv` around the layer, `"auto"` does not neutralize: see {@link CliLogOptions.runtimeEnv}.
1803
+ *
1804
+ * @param options - the level, the env var, the format, the `CliLogger` options and the optional file sink
50
1805
  */
51
- static readonly formatterLayer: (overrides?: Partial<CliOutput.Formatter>) => Layer.Layer<never, never, Stdio.Stdio>;
1806
+ static layer(options: CliLogOptions & {
1807
+ readonly format: "json";
1808
+ readonly file?: undefined;
1809
+ }): Layer.Layer<never, never, never>;
1810
+ /**
1811
+ * The logger set with NDJSON diagnostics and a file sink; see the first overload.
1812
+ *
1813
+ * @remarks
1814
+ * Without a `CurrentRuntimeEnv` around the layer, `"auto"` does not neutralize: see {@link CliLogOptions.runtimeEnv}.
1815
+ *
1816
+ * @param options - the level, the env var, the `CliLogger` options and the file sink
1817
+ */
1818
+ static layer(options: CliLogFileOptions & {
1819
+ readonly format: "json";
1820
+ }): Layer.Layer<never, never, FileSystem.FileSystem | Path.Path>;
1821
+ /**
1822
+ * The logger set with pretty diagnostics; see the first overload.
1823
+ *
1824
+ * @remarks
1825
+ * Without a `CurrentRuntimeEnv` around the layer, `"auto"` does not neutralize: see {@link CliLogOptions.runtimeEnv}.
1826
+ *
1827
+ * @param options - the level, the env var and the `CliLogger` options
1828
+ */
1829
+ static layer(options: CliLogOptions & {
1830
+ readonly format: "pretty";
1831
+ readonly file?: undefined;
1832
+ }): Layer.Layer<never, never, TerminalEnv>;
1833
+ /**
1834
+ * The logger set with pretty diagnostics and a file sink; see the first overload.
1835
+ *
1836
+ * @remarks
1837
+ * Without a `CurrentRuntimeEnv` around the layer, `"auto"` does not neutralize: see {@link CliLogOptions.runtimeEnv}.
1838
+ *
1839
+ * @param options - the level, the env var, the `CliLogger` options and the file sink
1840
+ */
1841
+ static layer(options: CliLogFileOptions & {
1842
+ readonly format: "pretty";
1843
+ }): Layer.Layer<never, never, TerminalEnv | FileSystem.FileSystem | Path.Path>;
1844
+ /**
1845
+ * The logger set with the format decided by the audience; see the first overload.
1846
+ *
1847
+ * @remarks
1848
+ * Without a `CurrentRuntimeEnv` around the layer, `"auto"` does not neutralize: see {@link CliLogOptions.runtimeEnv}.
1849
+ *
1850
+ * @param options - the level, the env var, the format and the `CliLogger` options
1851
+ */
1852
+ static layer(options?: CliLogOptions & {
1853
+ readonly file?: undefined;
1854
+ }): Layer.Layer<never, never, Audience | TerminalEnv>;
1855
+ /**
1856
+ * The logger set with the format decided by the audience, and a file sink; see the first overload.
1857
+ *
1858
+ * @remarks
1859
+ * Without a `CurrentRuntimeEnv` around the layer, `"auto"` does not neutralize: see {@link CliLogOptions.runtimeEnv}.
1860
+ *
1861
+ * @param options - the level, the env var, the format, the `CliLogger` options and the file sink
1862
+ */
1863
+ static layer(options: CliLogFileOptions): Layer.Layer<never, never, Audience | TerminalEnv | FileSystem.FileSystem | Path.Path>;
1864
+ /**
1865
+ * Mark the log records an effect emits as coming from `name`.
1866
+ *
1867
+ * @remarks
1868
+ * Shown as `[name]` in pretty output and as `annotations.component` in NDJSON.
1869
+ *
1870
+ * @param name - the component
1871
+ */
1872
+ static readonly component: (name: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
1873
+ }
1874
+ //#endregion
1875
+ //#region src/CliEnv.d.ts
1876
+ /**
1877
+ * Options for {@link CliEnv.layer} and for `CliRuntime.main`'s `env` option.
1878
+ *
1879
+ * @public
1880
+ */
1881
+ interface CliEnvOptions {
1882
+ /** The environment variable that overrides the audience, read through `Config`; see `Audience.layer`. */
1883
+ readonly audienceEnvVar?: string | undefined;
1884
+ /**
1885
+ * Overrides the stderr terminal check; `Stdio` reports only stdout, so stderr mirrors it otherwise.
1886
+ *
1887
+ * @remarks
1888
+ * Mirroring means a program whose stdout is a terminal but whose stderr is redirected to a file still paints
1889
+ * stderr (a failure report, a warning) with colour escapes. A Node host that redirects stderr passes the real
1890
+ * answer: `env: { stderrIsTerminal: Effect.sync(() => process.stderr.isTTY === true) }`. Core's `Stdio` has no
1891
+ * stderr terminal check to read instead; that gap is tracked upstream as Effect-TS/effect#8639.
1892
+ */
1893
+ readonly stderrIsTerminal?: Effect.Effect<boolean> | undefined;
1894
+ /** Options for the theme; see {@link CliThemeOptions}. */
1895
+ readonly theme?: CliThemeOptions | undefined;
1896
+ /**
1897
+ * Diagnostics options. Only `CliRuntime.main` reads this: when given, `main` uses `CliLog.layer` with these
1898
+ * options as the program's logger, instead of the default `CliLogger.layer()`. The platform is built under
1899
+ * that logger, so a line it logs while building goes to stderr.
1900
+ *
1901
+ * @remarks
1902
+ * It may carry the `file` option, which also writes an async NDJSON file. The platform must then provide
1903
+ * `FileSystem` and `Path`, and `main`'s type says so when it does not.
1904
+ *
1905
+ * A platform or program that installs its own `Logger.layer([...])` replaces this logger set, and the
1906
+ * diagnostics go silent with no error: do not install one. See `CliLog.layer`. Only `CliLog`'s own records can be
1907
+ * silenced (`plainLogger: false`): what the platform logs while it builds goes through the full `CliLog` when its
1908
+ * build-time format is NDJSON (`json`, or `auto` for an agent or a CI; see `CliLogOptions.format`) and through a plain
1909
+ * `CliLogger` otherwise, routed by `logger.stderrFrom` as the host set it. The audience-override warning is never silenced: it is written exactly once, in NDJSON when
1910
+ * the build-time format is NDJSON and as a plain line otherwise, to stderr alone (never stdout, whatever
1911
+ * `logger.stderrFrom` says) and never to the host's `extraLoggers` or log file. The failure report and the `CliMessage` lines always
1912
+ * go through a plain `CliLogger`.
1913
+ */
1914
+ readonly log?: CliLogOptions | CliLogFileOptions | undefined;
1915
+ /**
1916
+ * Methods of core's `CliOutput.Formatter` to replace in the one `CliRuntime.main` installs, for example
1917
+ * `formatVersion` to name the carrier a bin runs through. Only `CliRuntime.main` reads this.
1918
+ *
1919
+ * @remarks
1920
+ * `main` installs a coloured default formatter inside the platform, which shadows any formatter the platform
1921
+ * sets; this is the way to keep a method of your own. Methods you omit keep the coloured defaults.
1922
+ */
1923
+ readonly formatter?: Partial<CliOutput.Formatter> | undefined;
1924
+ /**
1925
+ * Turns an absolute path into its display form, for example relative to the workspace: the stack frames of the
1926
+ * default failure report are shown through it. Only `CliRuntime.main` reads this. The identity by default.
1927
+ */
1928
+ readonly displayPath?: ((absolute: string) => string) | undefined;
1929
+ /**
1930
+ * Which frames of a defect's stack the default failure report shows: `app`, the default, leaves out every
1931
+ * `node_modules` frame (Effect's and any other dependency's) and the runtime's own (every `node:` frame and every
1932
+ * frame with no file), and prints how many it left out after the frames it shows, as
1933
+ * `(+N internal frames hidden)`, or `no user frames (N internal frames hidden)` when none is left; `all` shows
1934
+ * every frame. Applies to the report `main` writes, `FailureDetails.defaultLines` and `FailureDetails.lines`. Only
1935
+ * `CliRuntime.main` reads this.
1936
+ */
1937
+ readonly stackFrames?: "app" | "all" | undefined;
1938
+ /** Whether file links open in an editor; `auto` by default. See {@link CliLinks}. */
1939
+ readonly editorLinks?: EditorLinks | undefined;
1940
+ /** The environment variable that overrides `editorLinks`, read through `Config`. Not read unless named. */
1941
+ readonly editorLinksEnvVar?: string | undefined;
1942
+ }
1943
+ /**
1944
+ * The services {@link CliEnv.layer} provides.
1945
+ *
1946
+ * @public
1947
+ */
1948
+ type CliEnvServices = CurrentRuntimeEnv | TerminalEnv | Audience | CliTheme | CliLinks | Terminal.Terminal;
1949
+ /**
1950
+ * Options for {@link CliEnv.layerTest}: the answers a test fixes. Every field has a quiet default.
1951
+ *
1952
+ * @public
1953
+ */
1954
+ interface CliEnvTestOptions {
1955
+ /** Whether standard input, output and error are all terminals; `false` by default, as for a pipe. */
1956
+ readonly tty?: boolean | undefined;
1957
+ /**
1958
+ * The `TERM` the layer's own builds read: `dumb` makes a terminal not interactive and, with the theme's glyphs at
1959
+ * `auto`, draws ASCII. Unset by default, whatever the host's `TERM` is.
1960
+ */
1961
+ readonly term?: string | undefined;
1962
+ /** The audience; `human` by default. */
1963
+ readonly audience?: AudienceKind | undefined;
1964
+ /** The width of the terminal, in columns; none by default, so a width falls back to the reader's (80). */
1965
+ readonly columns?: number | undefined;
1966
+ /** The colour level of both stdout and stderr; `none` by default. Fixed as given: `term` does not change it. */
1967
+ readonly color?: ColorLevel | undefined;
1968
+ /** Options for the theme, as {@link CliEnvOptions.theme}; its glyphs are `auto` by default, so `term` decides. */
1969
+ readonly theme?: CliThemeOptions | undefined;
1970
+ }
1971
+ /**
1972
+ * The services {@link CliEnv.layerTest} provides.
1973
+ *
1974
+ * @public
1975
+ */
1976
+ type CliEnvTestServices = TerminalEnv | Audience | CliTheme;
1977
+ /**
1978
+ * The environment services a CLI reads, built once and in the right order.
1979
+ *
1980
+ * @remarks
1981
+ * Builds `CurrentRuntimeEnv`, `TerminalEnv`, `Audience`, `CliTheme` and `CliLinks`, and sets `CliInteractive` from them, then
1982
+ * installs the two gates for the program: `CliPrompt.gateTerminal`, which replaces `Terminal` with a quiet one when
1983
+ * the run is not interactive so no prompt runner ever attaches to stdin, and `CliPrompt.gateWizard`, which drops
1984
+ * `--wizard` then. `TerminalEnv` is built from the real terminal first. The layer therefore also outputs
1985
+ * `Terminal`, the gated one, and consumers never compose the gates themselves. A
1986
+ * `Context.Reference`'s key type is `never`, so the layer's output type does not list `CliInteractive`: it sets
1987
+ * the reference rather than providing a service. Every read of the environment goes through `Config` and
1988
+ * degrades to "unset" when it fails, so building the layer does not fail on a bad provider; it fails only when
1989
+ * `Stdio` or `Terminal` do.
1990
+ *
1991
+ * `CliLinks` reads `FileSystem` and `Path` from the surrounding context if it has them, and does not require them: a
1992
+ * `.vscode/` directory is looked for, and a relative path resolved, only when the platform is provided OUTSIDE this
1993
+ * layer, as `CliRuntime.main` does. Without them `auto` is `vscode` on the terminal signal alone.
1994
+ *
1995
+ * Not interactive, the gated `Terminal`'s `readLine` fails as a quit, its input is already ended and its `display`
1996
+ * writes nothing. A program that reads piped data must read `Stdio.stdin`, and one that writes output must use
1997
+ * `Console` or `Stdio`, never `Terminal`. It also installs `CliTheme.promptTheme`, so core's prompts follow the
1998
+ * theme.
1999
+ *
2000
+ * @public
2001
+ */
2002
+ export declare class CliEnv {
2003
+ private constructor();
2004
+ /**
2005
+ * The environment services for the terminal `Stdio` and `Terminal` describe.
2006
+ *
2007
+ * @remarks
2008
+ * A layer-returning function mints a fresh layer per call: call it once and bind the result to a constant.
2009
+ *
2010
+ * @param options - the audience env var, the stderr check and the theme options
2011
+ */
2012
+ static readonly layer: (options?: CliEnvOptions) => Layer.Layer<CliEnvServices, never, Stdio.Stdio | Terminal.Terminal>;
2013
+ /**
2014
+ * The environment services a test fixes, needing nothing and reading nothing of the host's: `TerminalEnv` and
2015
+ * `Audience` from the answers given, `CliTheme` built from them as {@link CliEnv.layer} builds it, and
2016
+ * `CliInteractive` set from them by the same rule (a human, every stream a terminal, and a `TERM` that is not
2017
+ * `dumb`).
2018
+ *
2019
+ * @remarks
2020
+ * `term` is handed to the theme and interactivity builds alone, through a `ConfigProvider` of their own: the
2021
+ * program under the layer keeps its own provider, and a host's `TERM` (a test runner in a dumb terminal) never
2022
+ * decides. A screen or a live view also needs `UiStreams` from `@effected/cli/ui`, which `CliUiTest` provides; this
2023
+ * layer provides no `Terminal` and installs neither of `CliEnv.layer`'s prompt gates.
2024
+ *
2025
+ * A layer-returning function mints a fresh layer per call: call it once and bind the result to a constant.
2026
+ *
2027
+ * @param options - whether the streams are terminals, the `TERM`, the audience, the width, the colour and the theme
2028
+ */
2029
+ static readonly layerTest: (options?: CliEnvTestOptions) => Layer.Layer<CliEnvTestServices>;
52
2030
  }
53
2031
  //#endregion
54
2032
  //#region src/CliExit.d.ts
@@ -71,7 +2049,7 @@ declare const CliExit_base: Context.ServiceClass<CliExit, "@effected/cli/CliExit
71
2049
  * handler must return normally — yet the process must exit non-zero. Writing
72
2050
  * `process.exitCode` works only because Node's `runMain` skips
73
2051
  * `process.exit(0)` on success; `process.exit(n)` in a handler skips every
74
- * finalizer. {@link CliRuntime.main} reads this cell after the program
2052
+ * finalizer. `CliRuntime.main` reads this cell after the program
75
2053
  * succeeds and turns a non-zero code into a marked failure the runtime's
76
2054
  * teardown honours, on any runtime, with finalizers intact.
77
2055
  *
@@ -117,109 +2095,301 @@ export declare class CliExit extends CliExit_base {
117
2095
  static readonly set: (code: number) => Effect.Effect<void, never, CliExit>;
118
2096
  }
119
2097
  //#endregion
120
- //#region src/CliLogger.d.ts
2098
+ //#region src/CliFailure.d.ts
121
2099
  /**
122
- * How a log record is turned into a line.
2100
+ * The protocol an error class implements to say how a failure is shown: a method under this key that returns the
2101
+ * document.
2102
+ *
2103
+ * @remarks
2104
+ * `CliFailure.toDoc` calls it for a typed failure that has one, so an application error draws itself (a heading, a
2105
+ * table of what went wrong) and the default report uses it with no registration. A `Symbol.for` key, so two copies
2106
+ * of this package agree on it.
123
2107
  *
124
2108
  * @public
125
2109
  */
126
- interface CliLoggerOptions {
2110
+ export declare const CliDoc: unique symbol;
2111
+ /**
2112
+ * An error that draws itself: see {@link CliDoc}.
2113
+ *
2114
+ * @public
2115
+ */
2116
+ interface CliDocSource {
2117
+ readonly [CliDoc]: () => Document;
2118
+ }
2119
+ /**
2120
+ * Options for {@link CliFailure.toDoc}.
2121
+ *
2122
+ * @public
2123
+ */
2124
+ interface CliFailureOptions {
127
2125
  /**
128
- * Render one message. Defaults to joining an array with spaces and
129
- * `String`-ing anything else.
130
- *
131
- * @remarks
132
- * An array arrives because `Effect.log("synced", 3, "repos")` is variadic.
2126
+ * A document per error `_tag`, for errors that do not implement {@link CliDoc}. A tag with no entry is shown by
2127
+ * the default rules.
133
2128
  */
134
- readonly render?: ((message: unknown) => string) | undefined;
2129
+ readonly render?: Readonly<Record<string, (error: unknown) => Document>> | undefined;
2130
+ /** Turns an absolute path into its display form, for the stack frames; the identity by default. */
2131
+ readonly displayPath?: ((absolute: string) => string) | undefined;
135
2132
  /**
136
- * The level at and above which output goes to stderr. Defaults to `"All"`,
137
- * so every log level is a diagnostic and stdout carries only what the
138
- * program writes with `Console.log`.
139
- *
140
- * @remarks
141
- * Pass `"Error"` to restore the old split, for a tool whose output *is*
142
- * its log lines rather than a separate document written with
143
- * `Console.log`.
2133
+ * Which frames of a defect's stack are shown. `app`, the default, shows the program's own: every `node_modules`
2134
+ * frame (Effect's and any other dependency's), `node:internal` and the runtime's generator frames are left out and
2135
+ * counted. When that leaves no frame, as for a program run from its install under `node_modules` (a global
2136
+ * install, `npx`, a pnpm store), only the runtime's and Effect's are left out, so its own frames still show. `all`
2137
+ * shows every frame, unfiltered.
144
2138
  */
145
- readonly stderrFrom?: LogLevel.LogLevel | undefined;
2139
+ readonly stackFrames?: "app" | "all" | undefined;
146
2140
  }
147
2141
  /**
148
- * A `Logger` that renders CLI output rather than service logs.
2142
+ * A failure as a document: what the default report prints, and a building block for a custom one.
149
2143
  *
150
2144
  * @remarks
151
- * Effect's default logger emits `[00:33:56.619] INFO (#2): message`. That is
152
- * the right shape for a long-running service being scraped and the wrong one
153
- * for a tool a person is watching: the timestamp, level and fiber id are noise
154
- * in front of output a human is reading, and they make a formatted block — a
155
- * permissions table, a summary — unreadable.
2145
+ * One run of blocks per `Cause` reason. A typed failure is, in order of preference: the document of an error that
2146
+ * implements {@link CliDoc}; the document `options.render` holds for its `_tag`; for `Cancelled` and
2147
+ * `NotInteractive`, their one fixed line; a `Tree` of the rejected values, for a schema error or issue; else a failure
2148
+ * status line with its message. A defect is its message followed by a collapsible `stack` of the program's own frames,
2149
+ * each a file link (so a terminal can open it in an editor) shown through `displayPath`, with the runtime's frames (every
2150
+ * `node:` frame and every frame with no file) and every `node_modules` frame (Effect's and any other dependency's)
2151
+ * left out unless `stackFrames` is `all`. A frame is classified by its file alone, never by its function name, so a
2152
+ * program's own thunk that V8 names by Effect's method alias is still shown. When that would
2153
+ * leave no frame at all, as for a program run from its own install under `node_modules`, only the runtime's and
2154
+ * Effect's are left out. Then an
2155
+ * `Error.cause` chain as a tree. When cleaning leaves no frame the stack says
2156
+ * `no user frames (N internal frames hidden)`, never an empty block; when frames survive and some were left out, the
2157
+ * count follows them as `(+N internal frames hidden)`. A reason that ran under spans is followed by
2158
+ * `in: outer › inner`. Interrupts are not rendered beside a real failure, and a cause with only interrupts is the
2159
+ * one line `interrupted`.
2160
+ *
2161
+ * All text goes through the document, so a control character in a message or a stack frame never reaches the terminal.
2162
+ * Render it with `Render.context` and `Render.plain`, `ansi`, `markdown` or `githubLog`, or `Doc.print` it.
156
2163
  *
157
- * **This is not a preference, and it is not visible from the call site.** A
158
- * program that never installs a CLI logger looks correct in review and ships
159
- * timestamps to its users; the consumer this package was extracted from had it
160
- * missing for a day while its own docs claimed it existed.
2164
+ * @public
2165
+ */
2166
+ export declare class CliFailure {
2167
+ private constructor();
2168
+ /**
2169
+ * Build the document of a cause.
2170
+ *
2171
+ * @param cause - the failure
2172
+ * @param options - per-tag documents and a path display function
2173
+ */
2174
+ static readonly toDoc: (cause: Cause.Cause<unknown>, options?: CliFailureOptions) => Document;
2175
+ }
2176
+ //#endregion
2177
+ //#region src/CliInteractive.d.ts
2178
+ declare const CliInteractive_base: Context.Reference<boolean>;
2179
+ /**
2180
+ * Whether this run may prompt a person: a human audience, with a terminal on
2181
+ * both standard input and standard output, and a `TERM` that is not `dumb`.
161
2182
  *
162
- * ## Why the `Console` reference, and not `Stdio`
2183
+ * @remarks
2184
+ * A `Context.Reference`, not a `Context.Service`, for three reasons. It is one
2185
+ * boolean with a safe default, which is what a reference is for. A scoped
2186
+ * override, {@link CliInteractive.unless}, is a plain
2187
+ * `Effect.provideService`, so a command can switch prompting off for one
2188
+ * subtree without a layer. And forgetting to provide it is not a type error
2189
+ * but a harmless answer, since it reads `false` when no layer is provided: a
2190
+ * program that never wired it refuses to prompt rather than hanging on a
2191
+ * terminal that is not there. Read it with `yield* CliInteractive`.
163
2192
  *
164
- * The obvious design — write through `Stdio`'s `stdout()` / `stderr()` sinks —
165
- * does not fit. `Logger.make` takes a **synchronous** callback and a `Sink`
166
- * write is an `Effect`: a logger cannot `yield*`.
2193
+ * Because a reference's key type is `never`, {@link CliInteractive.layer} and
2194
+ * {@link CliInteractive.layerTest} are typed `Layer<never>`: they set the
2195
+ * reference rather than provide a service.
167
2196
  *
168
- * Writing to `process.stdout` would fit, and is what the consumer did first,
169
- * but it drags a platform assumption into a library and — the part that
170
- * actually matters — makes the stream split **untestable**, because asserting
171
- * it means monkey-patching a global inside a runner that is itself writing to
172
- * those streams.
2197
+ * @public
2198
+ */
2199
+ export declare class CliInteractive extends CliInteractive_base {
2200
+ /**
2201
+ * Decide from the audience and the terminal: `true` only for a human audience with a terminal on both
2202
+ * standard input and standard output, and a `TERM` that is not `dumb`.
2203
+ *
2204
+ * @remarks
2205
+ * A dumb terminal is a terminal, but it cannot move the cursor or take synchronized output, which a prompt or a
2206
+ * screen redrawing in place needs: it gets what a pipe gets. `TERM` is read through the ambient `ConfigProvider`,
2207
+ * as `@effected/env` reads the environment, so it adds no requirement; a test fixes it with
2208
+ * `Effect.provideService(ConfigProvider.ConfigProvider, ConfigProvider.fromUnknown({ TERM: "dumb" }))`.
2209
+ *
2210
+ * Bind the layer to a constant and provide it once; `Audience` and `TerminalEnv` come from `@effected/env`.
2211
+ */
2212
+ static readonly layer: Layer.Layer<never, never, Audience | TerminalEnv>;
2213
+ /**
2214
+ * A fixed answer that needs nothing.
2215
+ *
2216
+ * @param value - whether the run is interactive
2217
+ */
2218
+ static readonly layerTest: (value: boolean) => Layer.Layer<never>;
2219
+ /**
2220
+ * Run `self` with interactivity switched off when `condition` holds.
2221
+ *
2222
+ * @remarks
2223
+ * It only narrows: `unless(false)` leaves the current value alone and never turns interactivity on, so a
2224
+ * non-interactive scope stays non-interactive. The outer value is restored when `self` ends, whether it
2225
+ * succeeds, fails or is interrupted.
2226
+ *
2227
+ * A flag that resolves the audience (`--human`, under `CliAudience.runWith` or `provide`) recomputes interactivity
2228
+ * from the terminal facts, so it can override an outer `unless` or a `layerTest(false)`: those narrow the
2229
+ * environment's answer, and the flag is a later, explicit one.
2230
+ *
2231
+ * @param condition - `true` to switch prompting off for `self`
2232
+ */
2233
+ static readonly unless: (condition: boolean) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
2234
+ }
2235
+ //#endregion
2236
+ //#region src/CliMessage.d.ts
2237
+ /**
2238
+ * Options for {@link CliMessage.status}.
173
2239
  *
174
- * So this takes the path core's own `defaultLogger` takes: read the `Console`
175
- * off the fiber, synchronously. `Console.Console` is a public
176
- * `Context.Reference`, so it carries a default and never appears in `R`, and a
177
- * test swaps the reference instead of stubbing a global.
2240
+ * @public
2241
+ */
2242
+ interface CliMessageOptions {
2243
+ /**
2244
+ * Where the line goes. Defaults to stderr for a status whose rank is at or above `warning`'s in its
2245
+ * vocabulary, stdout otherwise.
2246
+ */
2247
+ readonly stream?: "stdout" | "stderr" | undefined;
2248
+ }
2249
+ /**
2250
+ * One-line status messages: a glyph and some text, themed for a person and plain for an agent.
178
2251
  *
179
- * @example
180
- * ```ts
181
- * import { CliLogger } from "@effected/cli"
182
- * import { Console, Effect } from "effect"
2252
+ * @remarks
2253
+ * Each line goes through `Console`, `log` for stdout and `error` for stderr, never through the logger, so no
2254
+ * log level can silence it. Only the glyph is painted; the text stays plain. An `agent` audience gets the glyph
2255
+ * and the text and never colour, even when the theme has colour. `success` and `info` go to stdout, `warning`
2256
+ * and `failure` to stderr.
183
2257
  *
184
- * const program = Effect.gen(function* () {
185
- * yield* Effect.log("synced 3 repos") // stderr, no timestamp — a diagnostic
186
- * yield* Console.log("3 repos synced") // stdout — the program's actual output
187
- * yield* Effect.logError("one failed") // stderr
188
- * })
2258
+ * The text is whatever the caller supplies, so it is sanitised: escape sequences and control characters are removed
2259
+ * (a line break is kept as one, a tab becomes a space), as in a document. Under GitHub Actions, where
2260
+ * `CurrentRuntimeEnv` says so, a line the runner would read as a workflow command is neutralized as well. The glyphs
2261
+ * come from the vocabulary, which is configuration, and are not.
189
2262
  *
190
- * program.pipe(Effect.provide(CliLogger.layer()))
191
- * ```
2263
+ * @public
2264
+ */
2265
+ export declare class CliMessage {
2266
+ private constructor();
2267
+ /**
2268
+ * Print a status line from a vocabulary.
2269
+ *
2270
+ * @remarks
2271
+ * The stream defaults to stderr when the status ranks at or above `warning` in `vocab`, and stdout
2272
+ * otherwise, so a custom status follows its own rank: a `timeout` ranked 85 goes to stderr.
2273
+ *
2274
+ * @param vocab - the vocabulary the status belongs to
2275
+ * @param name - the status
2276
+ * @param text - the text after the glyph
2277
+ * @param options - the stream override
2278
+ */
2279
+ static readonly status: <N extends string>(vocab: Status<N>, name: N, text: string, options?: CliMessageOptions) => Effect.Effect<void, never, CliTheme | Audience>;
2280
+ /**
2281
+ * A success line, on stdout.
2282
+ *
2283
+ * @param text - the text after the glyph
2284
+ */
2285
+ static readonly success: (text: string) => Effect.Effect<void, never, CliTheme | Audience>;
2286
+ /**
2287
+ * An informational line, on stdout.
2288
+ *
2289
+ * @param text - the text after the glyph
2290
+ */
2291
+ static readonly info: (text: string) => Effect.Effect<void, never, CliTheme | Audience>;
2292
+ /**
2293
+ * A warning line, on stderr.
2294
+ *
2295
+ * @param text - the text after the glyph
2296
+ */
2297
+ static readonly warning: (text: string) => Effect.Effect<void, never, CliTheme | Audience>;
2298
+ /**
2299
+ * A failure line, on stderr.
2300
+ *
2301
+ * @param text - the text after the glyph
2302
+ */
2303
+ static readonly failure: (text: string) => Effect.Effect<void, never, CliTheme | Audience>;
2304
+ }
2305
+ //#endregion
2306
+ //#region src/CliPrompt.d.ts
2307
+ /**
2308
+ * Which missing parameter a fallback stands in for, so a non-interactive run can fail with core's own error.
192
2309
  *
193
- * @remarks
194
- * `stderrFrom` defaults to `"All"`: a CLI's stdout is its product, so every
195
- * log level is a diagnostic unless a consumer narrows the threshold. Write
196
- * program output with `Console.log`, never `Effect.log`. Pass
197
- * `stderrFrom: "Error"` for a tool whose output *is* its log lines. This is
198
- * a breaking change on the 0.x line (#716).
2310
+ * @public
2311
+ */
2312
+ type CliPromptTarget = {
2313
+ readonly flag: string;
2314
+ } | {
2315
+ readonly argument: string;
2316
+ };
2317
+ /**
2318
+ * Options for {@link CliPrompt.fallback}.
199
2319
  *
200
2320
  * @public
201
2321
  */
202
- export declare class CliLogger {
2322
+ type CliPromptFallbackOptions<A> = CliPromptTarget & {
2323
+ /** The value to use when the run is not interactive. Without it a non-interactive run fails as missing. */
2324
+ readonly otherwise?: A;
2325
+ };
2326
+ /**
2327
+ * Prompts that know whether there is a person to ask.
2328
+ *
2329
+ * @public
2330
+ */
2331
+ export declare class CliPrompt {
203
2332
  private constructor();
204
2333
  /**
205
- * The logger itself, for composing into an existing `Logger.layer` set.
2334
+ * A fallback for `Flag.withFallbackPrompt` or `Argument.withFallbackPrompt` that only prompts when the run is
2335
+ * interactive.
206
2336
  *
207
2337
  * @remarks
208
- * Prefer {@link CliLogger.layer}. Reach for this only when you are building
209
- * the logger set yourself and want this one among several.
2338
+ * Interactive (`CliInteractive`): the prompt runs and its answer is used. Not interactive: `otherwise` is used
2339
+ * when given, and the terminal is never read; without it the parameter fails as missing, exactly as it would
2340
+ * with no fallback, so core renders its own message and `CliRuntime.main` exits `64`. Name the parameter with
2341
+ * `flag` (the name without dashes, as given to `Flag.String`) or `argument` so that error can be built.
2342
+ *
2343
+ * Why the prompt runs inside this function rather than being handed back to core: core's
2344
+ * `withFallbackPrompt` turns a quit, such as Ctrl-C, into the original missing-parameter error, which would
2345
+ * exit `64` as if the flag had been forgotten. Here a quit is `Cancelled` with reason `interrupt`, exit `130`.
2346
+ * It is raised as a defect because core's parse step turns every typed failure into a usage error. Core then
2347
+ * runs the already-answered `Prompt.succeed` it is handed. Because it travels as a defect, a handler's
2348
+ * `Effect.catchTag("Cancelled", ...)` cannot see it, and only `CliRuntime.main` (or
2349
+ * `CliRuntime.reportFailures`) renders it as one line with exit `130`; under a bare `runMain` it prints a
2350
+ * stack.
2351
+ *
2352
+ * Pair it with `CliPrompt.gateTerminal`, which `CliEnv.layer` installs: core still runs `Prompt.run` on the
2353
+ * answered prompt it is handed, and `Prompt.run` subscribes the terminal's input, which on a real terminal
2354
+ * attaches a reader to stdin and drops piped input. The gate makes that subscription harmless when the run is
2355
+ * not interactive.
2356
+ *
2357
+ * @param prompt - the prompt to show
2358
+ * @param options - the parameter it stands in for, and the non-interactive default
210
2359
  */
211
- static readonly make: (options?: CliLoggerOptions) => Logger.Logger<unknown, void>;
2360
+ static readonly fallback: <A>(prompt: Prompt.Prompt<A>, options: CliPromptFallbackOptions<A>) => Param.FallbackPrompt<A>;
212
2361
  /**
213
- * Replace the default logger with this one.
2362
+ * Gates core's `Terminal` on `CliInteractive`, so a run that is not interactive never touches the real one.
214
2363
  *
215
2364
  * @remarks
216
- * `Logger.layer` **replaces** rather than merges, so nothing is emitted twice.
2365
+ * Core's prompt runner subscribes the terminal's input even for a prompt that is already answered, and the
2366
+ * real Node terminal then attaches a readline to stdin, which drops piped input and puts a TTY stdin into raw
2367
+ * mode. Not interactive, this terminal's input is an already-ended queue, its `readLine` fails as quit and its
2368
+ * `display` writes nothing, so any prompt, the wizard included, is quit at once and the real terminal is never
2369
+ * read. Its `columns` and `rows` always come from the real one, so layout keeps working. Interactive, every
2370
+ * call passes through to the real terminal.
217
2371
  *
218
- * Merge this into the layer you provide to the whole program rather than
219
- * providing it beneath: merged, it also covers lines emitted during layer
220
- * construction, which is exactly where a startup failure prints.
2372
+ * The decision is made on every call, not when the layer is built, so a scope that narrows `CliInteractive`
2373
+ * later, such as an audience flag under `CliAudience.runWith`, gates it too.
2374
+ *
2375
+ * It requires the real `Terminal`. `CliEnv.layer` installs it, after `TerminalEnv` is built from the real
2376
+ * terminal, so consumers do not compose it.
221
2377
  */
222
- static readonly layer: (options?: CliLoggerOptions) => Layer.Layer<never>;
2378
+ static readonly gateTerminal: Layer.Layer<Terminal.Terminal, never, Terminal.Terminal>;
2379
+ /**
2380
+ * Drops core's `--wizard` built-in flag when the run is not interactive.
2381
+ *
2382
+ * @remarks
2383
+ * The wizard prompts, so offering it without a terminal only leads to a dead end. With the flag gone, core
2384
+ * treats `--wizard` as an unknown flag, a usage error that exits `64`, and lists it nowhere in `--help`.
2385
+ * The layer reads `CliInteractive` and the ambient `CliConfig` when it is built, so provide those first, for
2386
+ * example `CliPrompt.gateWizard.pipe(Layer.provide(CliInteractive.layer))`. It filters the ambient `CliConfig`,
2387
+ * so a consumer's own `builtIns` survive, and returns it untouched when interactive. An audience flag read
2388
+ * later is covered by `CliAudience.runWith`, which drops the wizard too. When this layer removes the flag it records
2389
+ * that it did, so `runWith` puts it back only when a flag turns interactivity on where this took it away: a
2390
+ * consumer who never had `Wizard` in their `builtIns` keeps it out.
2391
+ */
2392
+ static readonly gateWizard: Layer.Layer<never>;
223
2393
  }
224
2394
  //#endregion
225
2395
  //#region src/CliRuntime.d.ts
@@ -237,6 +2407,33 @@ interface FailureDetails {
237
2407
  * failure from the error channel.
238
2408
  */
239
2409
  readonly isDefect: boolean;
2410
+ /**
2411
+ * The report the kit writes for this failure when there is no `render`: for this run, in this audience, with its
2412
+ * colour, links and `displayPath`. A `render` that hands a failure back returns these lines unchanged, and the
2413
+ * output is then exactly the default report. It equals `lines()`; to drop the leading status and keep the run's
2414
+ * settings, use {@link FailureDetails.lines} with `status: false`.
2415
+ */
2416
+ readonly defaultLines: ReadonlyArray<string>;
2417
+ /**
2418
+ * The report the kit would write for this failure, rendered for this run: its audience, colour, links and
2419
+ * `displayPath`.
2420
+ *
2421
+ * @remarks
2422
+ * `lines()` is {@link FailureDetails.defaultLines}. With `status: false` the leading status glyph, or the `[FAIL]`
2423
+ * tag in plain text, is left off, so a render that puts its own prefix in front (the program's name, say) reads
2424
+ * cleanly and still gets the run's colour and paths. `CliRuntime.defaultRender` with `status: false` drops the
2425
+ * status too, but it has no run to read and renders plain with absolute paths.
2426
+ *
2427
+ * ```ts
2428
+ * const render = (_error: unknown, details: FailureDetails) =>
2429
+ * details.lines({ status: false }).map((line, i) => (i === 0 ? `prog: ${line}` : line))
2430
+ * ```
2431
+ *
2432
+ * @param options - `status: false` leaves off the leading status
2433
+ */
2434
+ readonly lines: (options?: {
2435
+ readonly status?: boolean | undefined;
2436
+ }) => ReadonlyArray<string>;
240
2437
  }
241
2438
  /**
242
2439
  * How a failure is turned into output and an exit code.
@@ -245,7 +2442,10 @@ interface FailureDetails {
245
2442
  */
246
2443
  interface ReportFailuresOptions {
247
2444
  /**
248
- * Render the failure. Defaults to `String(error)`, one line.
2445
+ * Render the failure. Defaults to the failure's document, `CliFailure.toDoc(cause)`, rendered for the
2446
+ * audience: a failure status line, a tree for a schema failure, a defect's message with its cleaned stack, and
2447
+ * the one fixed line each for `Cancelled` and `NotInteractive`. Under `CliRuntime.main` with `env` it is painted for
2448
+ * a person and plain for an agent or a CI; elsewhere it is plain. It is still written through the logger.
249
2449
  *
250
2450
  * @remarks
251
2451
  * Return several lines to print several: a config error's own message
@@ -256,6 +2456,12 @@ interface ReportFailuresOptions {
256
2456
  * failure can render as one line and a defect as a full report, without
257
2457
  * guessing from the error's shape. A renderer that takes only `error`
258
2458
  * still fits.
2459
+ *
2460
+ * What it returns is text the kit did not build, so the report applies the output policy to it: under GitHub
2461
+ * Actions a line the runner would read as a workflow command is neutralized (with no environment services at all,
2462
+ * always), and for an agent or a CI audience escape sequences are removed (GitHub Actions detects as `ci`). For a person the escapes you return are kept,
2463
+ * since the kit cannot tell your own colour from an injected sequence: a `render` must sanitise the data it
2464
+ * interpolates (an error message, a file name) itself.
259
2465
  */
260
2466
  readonly render?: ((error: unknown, details: FailureDetails) => string | ReadonlyArray<string>) | undefined;
261
2467
  /**
@@ -285,7 +2491,7 @@ interface ReportFailuresOptions {
285
2491
  readonly usageExitCode?: number | undefined;
286
2492
  }
287
2493
  /**
288
- * Options for {@link CliRuntime.main}.
2494
+ * Options for `CliRuntime.main`.
289
2495
  *
290
2496
  * @public
291
2497
  */
@@ -295,8 +2501,32 @@ interface MainOptions<RP, EP> extends ReportFailuresOptions {
295
2501
  * on it. Passed in so this package never imports a platform.
296
2502
  */
297
2503
  readonly platform: Layer.Layer<RP, EP>;
298
- /** The logger, provided outermost. Defaults to `CliLogger.layer()`. */
2504
+ /**
2505
+ * The logger, provided outermost. Defaults to `CliLogger.layer()`, or to `CliLog.layer(env.log)` when
2506
+ * `env.log` is given. An explicit `logger` wins over both.
2507
+ */
299
2508
  readonly logger?: Layer.Layer<never> | undefined;
2509
+ /**
2510
+ * Provide the environment services, built by {@link CliEnv.layer}, inside failure reporting, where the platform
2511
+ * sits, together with `CliColor.formatterLayer` so help text follows the same colour decision.
2512
+ *
2513
+ * @remarks
2514
+ * The program may then require `CurrentRuntimeEnv`, `TerminalEnv`, `Audience` and `CliTheme` and read
2515
+ * `CliInteractive`. Without it `CliInteractive` keeps its non-interactive default, so forgetting this wiring
2516
+ * gives a CLI that never prompts. With `env.log`, `main` uses `CliLog.layer` as the logger set, and `env.log`
2517
+ * may carry the `file` option when the platform provides `FileSystem` and `Path`. A failure building the env
2518
+ * layer renders as one line and exits through `exitCode`.
2519
+ *
2520
+ * Not interactive, the `Terminal` the program sees is gated: its `readLine` fails as a quit, its input is
2521
+ * already ended and its `display` writes nothing. A program that reads piped data must read `Stdio.stdin`, and
2522
+ * one that writes output must use `Console` or `Stdio`, never `Terminal`.
2523
+ *
2524
+ * Stderr's colour mirrors stdout's terminal check unless `env.stderrIsTerminal` says otherwise, so with stderr
2525
+ * redirected and stdout a terminal the failure report is painted into the file. On Node, pass the real check:
2526
+ * `env: { stderrIsTerminal: Effect.sync(() => process.stderr.isTTY === true) }` (core's `Stdio` reports only
2527
+ * stdout; upstream Effect-TS/effect#8639).
2528
+ */
2529
+ readonly env?: CliEnvOptions | undefined;
300
2530
  /**
301
2531
  * Where the help document goes when it is printed with a usage error:
302
2532
  * `"stdout"` (the default, core's behaviour) or `"stderr"`, beside the
@@ -321,7 +2551,7 @@ interface MainOptions<RP, EP> extends ReportFailuresOptions {
321
2551
  * Report a CLI program's failures through the program's own logger.
322
2552
  *
323
2553
  * @remarks
324
- * ## The bug this exists to prevent
2554
+ * ## What it prevents
325
2555
  *
326
2556
  * A platform `runMain` reports an unhandled failure using Effect's **default**
327
2557
  * logger. That logger sits **outside** the layers the program was provided —
@@ -338,18 +2568,7 @@ interface MainOptions<RP, EP> extends ReportFailuresOptions {
338
2568
  *
339
2569
  * The fix has to happen **inside** the effect, before any `runMain` sees it. So
340
2570
  * this is a combinator you apply to your program, and you still call your own
341
- * platform's runner:
342
- *
343
- * @example
344
- * ```ts
345
- * import { CliRuntime } from "@effected/cli"
346
- * import { NodeRuntime } from "@effect/platform-node"
347
- * import { Effect } from "effect"
348
- *
349
- * NodeRuntime.runMain(program.pipe(CliRuntime.reportFailures(), Effect.provide(MainLive)))
350
- * ```
351
- *
352
- * Wrapping `runMain` itself would drag a platform choice into a library that
2571
+ * platform's runner. Wrapping `runMain` itself would drag a platform choice into a library that
353
2572
  * has no business making one, and would make this package unusable from Bun or
354
2573
  * Deno for no gain.
355
2574
  *
@@ -392,13 +2611,51 @@ interface MainOptions<RP, EP> extends ReportFailuresOptions {
392
2611
  * raises is likewise never rendered; it only carries the exit code a
393
2612
  * successful program recorded through `CliExit`.
394
2613
  *
2614
+ * @example
2615
+ * ```ts
2616
+ * import { CliRuntime } from "@effected/cli"
2617
+ * import { NodeRuntime } from "@effect/platform-node"
2618
+ * import { Effect } from "effect"
2619
+ *
2620
+ * NodeRuntime.runMain(program.pipe(CliRuntime.reportFailures(), Effect.provide(MainLive)))
2621
+ * ```
2622
+ *
395
2623
  * @public
396
2624
  */
397
2625
  export declare class CliRuntime {
398
2626
  private constructor();
399
2627
  /**
400
- * Catch, render through the ambient logger, and re-fail with the exit code
401
- * and the no-double-report mark.
2628
+ * What `reportFailures` and `main` render a failure as when no `render` option is given, for a consumer's own
2629
+ * `render` to hand a failure back to.
2630
+ *
2631
+ * @remarks
2632
+ * The plain lines of `CliFailure.toDoc(details.cause)`: a failure status line, a `Tree` for a schema failure, a
2633
+ * defect's message with its cleaned `stack`, and the one fixed line each for `Cancelled` and `NotInteractive`.
2634
+ * It has no terminal to ask, so it is the plain rendering for an agent, with absolute paths; the report `main` writes
2635
+ * with no `render` option is the same document in the renderer the audience gets (painted for a person), and that
2636
+ * report is `details.defaultLines`: return those to hand a failure back with the run's colour, links and path
2637
+ * display. With `status: false` the leading status (the glyph, or `[FAIL]` in plain text) is left off, so a prefix
2638
+ * such as the program's name reads cleanly; for that AND the run's settings, use `details.lines({ status: false })`.
2639
+ * A custom `render` that only cares about its own errors delegates the rest:
2640
+ *
2641
+ * ```ts
2642
+ * const render = (error: unknown, details: FailureDetails) =>
2643
+ * error instanceof MyError ? myLines(error) : CliRuntime.defaultRender(error, details)
2644
+ * ```
2645
+ *
2646
+ * @param error - the squashed failure
2647
+ * @param details - what `render` is told about the failure; accepted so a delegating `render` passes both
2648
+ * arguments through unchanged (only `cause` and `isDefect` are read)
2649
+ * @param options - `status: false` leaves off the leading status glyph or `[FAIL]` tag
2650
+ */
2651
+ static readonly defaultRender: (error: unknown, details: Pick<FailureDetails, "cause" | "isDefect">, options?: {
2652
+ readonly status?: boolean | undefined;
2653
+ }) => string | ReadonlyArray<string>;
2654
+ /**
2655
+ * Catch a program's failure, render it through the ambient logger, and re-fail with the exit code and the
2656
+ * no-double-report mark.
2657
+ *
2658
+ * @param options - how to render the failure and which exit codes to use
402
2659
  */
403
2660
  static readonly reportFailures: (options?: ReportFailuresOptions) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, Error, R>;
404
2661
  /**
@@ -412,17 +2669,44 @@ export declare class CliRuntime {
412
2669
  * fallback code rather than escaping to the runtime's stack trace.
413
2670
  * - The logger is provided **outermost**, so it is present whichever branch
414
2671
  * fails.
2672
+ * - With the `env` option, `CliEnv.layer` and `CliColor.formatterLayer` are
2673
+ * provided beside the platform, inside failure reporting, so the program can
2674
+ * read the audience, terminal, theme and `CliInteractive`.
415
2675
  *
416
- * You still call your platform's runner:
2676
+ * You still call your platform's runner.
417
2677
  *
418
2678
  * @example
419
2679
  * ```ts
2680
+ * import { CliRuntime } from "@effected/cli"
2681
+ * import { NodeRuntime, NodeServices } from "@effect/platform-node"
2682
+ * import { Command } from "effect/cli"
2683
+ *
420
2684
  * NodeRuntime.runMain(
421
- * CliRuntime.main(Command.run(root, { version }), { platform: NodeServices.layer, exitCode: 3 }),
2685
+ * CliRuntime.main(Command.run(root, { version: "1.0.0" }), { platform: NodeServices.layer, exitCode: 3 }),
422
2686
  * )
423
2687
  * ```
2688
+ *
2689
+ * @param program - the CLI program, usually `Command.run(root, { version })`
2690
+ * @param options - the platform layer, and optionally the logger, environment services, exit codes and rendering
424
2691
  */
425
- static readonly main: <A, E, R, RP, EP>(program: Effect.Effect<A, E, R>, options: MainOptions<RP, EP>) => Effect.Effect<void, Error, Exclude<Exclude<R, CliExit>, RP>>;
2692
+ static main<A, E, R, RP, EP>(program: Effect.Effect<A, E, R>, options: MainOptions<RP, EP> & {
2693
+ readonly env?: undefined;
2694
+ }): Effect.Effect<void, Error, Exclude<Exclude<R, CliExit>, RP>>;
2695
+ static main<A, E, R, RP, EP>(program: Effect.Effect<A, E, R>, options: MainOptions<RP, EP> & {
2696
+ readonly env: CliEnvOptions & {
2697
+ readonly log: CliLogFileOptions;
2698
+ };
2699
+ }): Effect.Effect<void, Error, Exclude<Exclude<R, CliExit | CliEnvServices>, RP> | Exclude<Stdio.Stdio | Terminal.Terminal | FileSystem.FileSystem | Path.Path, RP>>;
2700
+ static main<A, E, R, RP, EP>(program: Effect.Effect<A, E, R>, options: MainOptions<RP, EP> & {
2701
+ readonly env: CliEnvOptions & {
2702
+ readonly log?: CliLogOptions & {
2703
+ readonly file?: undefined;
2704
+ };
2705
+ };
2706
+ }): Effect.Effect<void, Error, Exclude<Exclude<R, CliExit | CliEnvServices>, RP> | Exclude<Stdio.Stdio | Terminal.Terminal, RP>>;
2707
+ static main<A, E, R, RP, EP>(program: Effect.Effect<A, E, R>, options: MainOptions<RP, EP> & {
2708
+ readonly env: CliEnvOptions;
2709
+ }): Effect.Effect<void, Error, Exclude<Exclude<R, CliExit | CliEnvServices>, RP> | Exclude<Stdio.Stdio | Terminal.Terminal | FileSystem.FileSystem | Path.Path, RP>>;
426
2710
  /**
427
2711
  * Mark an error as already reported, carrying an exit code.
428
2712
  *
@@ -431,7 +2715,7 @@ export declare class CliRuntime {
431
2715
  * command that prints its own diagnostics, say — needs the same two marks
432
2716
  * and should not have to rediscover the inverted polarity.
433
2717
  *
434
- * Under {@link CliRuntime.main} or {@link CliRuntime.reportFailures}, do NOT
2718
+ * Under `CliRuntime.main` or {@link CliRuntime.reportFailures}, do NOT
435
2719
  * print the failure yourself before failing with it: `reportFailures`
436
2720
  * renders every error except a `ShowHelp` and a `CliError.UserError` whose
437
2721
  * reported mark is `false`, so it would print twice. Fail with the marked
@@ -465,35 +2749,21 @@ export declare class CliRuntime {
465
2749
  //#endregion
466
2750
  //#region src/ConfigIssueRenderer.d.ts
467
2751
  /**
468
- * Render a `@effected/config-file` validation failure.
2752
+ * Turns a `@effected/config-file` `ConfigValidationError` into one line per rejected value.
469
2753
  *
470
2754
  * @remarks
471
2755
  * `ConfigValidationError` carries the structured `issue` tree rather than a
472
- * string, which is the right design and leaves the consumer holding a tree it
473
- * has to turn into sentences. This is that step, and it is the same treatment
474
- * {@link SchemaIssueRenderer} gives a bare issue.
475
- *
476
- * **This module is the only thing in the package that references
477
- * `@effected/config-file`, deliberately.** The peer is declared optional, and
478
- * an optional peer whose import is reachable from a shared module is not
479
- * optional — it is a crash for every consumer who took the manifest at its word
480
- * and did not install it. So nothing else in this package imports this module;
481
- * only the entrypoint re-exports it, and the shared rendering lives in
482
- * `internal/format`.
483
- *
484
- * The import is additionally `import type`, so it is erased at build time and
485
- * the runtime reach is **zero** — a consumer without `@effected/config-file`
486
- * installed can import this module and call `render` on any value without the
487
- * resolver ever being asked for the package.
488
- *
489
- * ## Why this and not just the error's message
490
- *
491
- * `ConfigValidationError.message` names the file. It does not name the value,
492
- * and the difference is the whole diagnostic: a consumer that printed only the
493
- * message told users "your config is invalid, run the doctor command", and the
494
- * doctor command — which guessed from a hand-written list of known keys — could
495
- * only ever find a *misspelling*. A wrongly **shaped** value left the two
496
- * commands pointing at each other and neither saying what was wrong.
2756
+ * string, so a caller holds a tree it has to turn into sentences. This is that
2757
+ * step, the same treatment {@link SchemaIssueRenderer} gives a bare issue.
2758
+ *
2759
+ * `ConfigValidationError.message` names the file but not the value, and the
2760
+ * value is the diagnostic: printing only the message tells a user their config
2761
+ * is invalid without saying which value is wrong or how it is shaped.
2762
+ *
2763
+ * `@effected/config-file` is an optional peer, and this module only
2764
+ * `import type`s it, so the import is erased at build time. A consumer without
2765
+ * the package installed can import this module without the resolver being asked
2766
+ * for it.
497
2767
  *
498
2768
  * @example
499
2769
  * ```ts
@@ -519,39 +2789,521 @@ export declare class ConfigIssueRenderer {
519
2789
  *
520
2790
  * @remarks
521
2791
  * Takes the **error**, not its `issue`, because that is what a `catchTag`
522
- * hands you and because `issue` is typed `Schema.Defect` — reaching into it
523
- * at every call site is exactly the ceremony this removes.
524
- *
525
- * The parameter is the **typed** error rather than `unknown`. An earlier
526
- * draft wrote `ConfigValidationError | unknown` to be accommodating, which
527
- * collapses to plain `unknown` in TypeScript — so it accepted anything, said
528
- * nothing, and left the type import in the `.d.ts` earning nothing. Inside
2792
+ * hands you and because `issue` is typed `Schema.Defect`, which every call
2793
+ * site would otherwise have to reach into. Inside
529
2794
  * `Effect.catchTag("ConfigValidationError", …)` the error is already this
530
- * type, which is where this is called.
2795
+ * type.
531
2796
  *
532
- * It still cannot throw on a malformed value: the issue tree is validated by
2797
+ * It cannot throw on a malformed value: the issue tree is validated by
533
2798
  * a guard before it is read, so a renderer on an error path never becomes the
534
2799
  * reason a program dies.
535
2800
  */
536
2801
  static readonly render: (error: ConfigValidationError) => ReadonlyArray<string>;
537
2802
  }
538
2803
  //#endregion
2804
+ //#region src/Fmt.d.ts
2805
+ /**
2806
+ * Options for {@link Fmt.truncate}.
2807
+ *
2808
+ * @public
2809
+ */
2810
+ interface TruncateOptions {
2811
+ /** Marks the cut; `…` by default. Counted by display width. */
2812
+ readonly ellipsis?: string | undefined;
2813
+ }
2814
+ /**
2815
+ * Options for {@link Fmt.percent}.
2816
+ *
2817
+ * @public
2818
+ */
2819
+ interface PercentOptions {
2820
+ /** Decimal places to round to; `1` by default. A trailing zero decimal is dropped. */
2821
+ readonly digits?: number | undefined;
2822
+ /**
2823
+ * What the input is scaled to: `1` (the default) is a ratio from 0 to 1, `100` is already a percentage from 0
2824
+ * to 100, as istanbul's coverage numbers are.
2825
+ */
2826
+ readonly scale?: 1 | 100 | undefined;
2827
+ }
2828
+ /**
2829
+ * Small, pure formatting primitives for terminal output.
2830
+ *
2831
+ * @public
2832
+ */
2833
+ export declare class Fmt {
2834
+ private constructor();
2835
+ /**
2836
+ * Text made safe to lay out and print, exactly as the renderers make it: complete ANSI and OSC sequences removed,
2837
+ * every other control character removed (a stray ESC, BEL, BS, DEL, the C1 range) except line feed and carriage
2838
+ * return, and a tab turned into a space.
2839
+ *
2840
+ * @remarks
2841
+ * For a `render` or any other text a program builds by hand from data it does not control: an error message, a
2842
+ * file name, a value from the environment. Line breaks are kept, so split on them if the text must be one line.
2843
+ *
2844
+ * @param text - the text
2845
+ */
2846
+ static readonly sanitize: (text: string) => string;
2847
+ /**
2848
+ * The display width of `text` in terminal columns: wide East Asian characters and emoji count two,
2849
+ * combining marks and ANSI escapes count none.
2850
+ *
2851
+ * @param text - the text to measure
2852
+ */
2853
+ static readonly width: (text: string) => number;
2854
+ /**
2855
+ * Cut `text` to at most `width` columns, ending in `ellipsis` when it was cut.
2856
+ *
2857
+ * @remarks
2858
+ * Grapheme-safe: a grapheme, such as a wide character or an emoji ZWJ family, is kept whole or dropped
2859
+ * whole, so the result is never wider than `width`. Text that already fits is returned unchanged, colour
2860
+ * escapes included. Text that must be cut is cut as plain text: its ANSI escapes are dropped rather than cut
2861
+ * in half. When the ellipsis cannot fit, it is omitted and the text is cut to `width`; a `width` of 0 or
2862
+ * less gives `""`.
2863
+ *
2864
+ * @param text - the text to truncate
2865
+ * @param width - the most columns the result may take
2866
+ * @param options - the ellipsis
2867
+ */
2868
+ static readonly truncate: (text: string, width: number, options?: TruncateOptions) => string;
2869
+ /**
2870
+ * A duration in milliseconds as short human text: `250ms`, `1.2s`, `2s`, `1m 3s`, `2m`, `1h 2m`, `2h`.
2871
+ *
2872
+ * @remarks
2873
+ * Under a second it is whole milliseconds; under a minute, seconds to one decimal with a trailing `.0`
2874
+ * dropped; under an hour, minutes and whole seconds, with the seconds dropped when zero; from an hour, hours and
2875
+ * whole minutes, the seconds dropped. A value that rounds up to the next unit (`999.6` to a second, `59999` to a
2876
+ * minute, `3599999` to an hour) is written in that unit, so `1000ms`, `60s` and `60m` never appear. There is no
2877
+ * days unit. A negative or non-finite input is `0ms`.
2878
+ *
2879
+ * @param ms - the duration in milliseconds
2880
+ */
2881
+ static readonly duration: (ms: number) => string;
2882
+ /**
2883
+ * A ratio from 0 to 1 as a percentage, or a number already from 0 to 100 with `scale: 100`: `83.3%`, `50%`,
2884
+ * `100%`.
2885
+ *
2886
+ * @remarks
2887
+ * Rounded to `digits` decimal places (one by default) with trailing zeros dropped, so whole values print
2888
+ * without a decimal. A value that rounds to zero prints `0%`, never `-0%`. The input is not validated.
2889
+ *
2890
+ * @param n - the ratio, 0 to 1 (0 to 100 with `scale: 100`)
2891
+ * @param options - the decimal places and the scale
2892
+ */
2893
+ static readonly percent: (n: number, options?: PercentOptions) => string;
2894
+ /**
2895
+ * A count with its noun: `1 test`, `2 tests`, `0 tests`.
2896
+ *
2897
+ * @param n - the count
2898
+ * @param singular - the noun for exactly one
2899
+ * @param plural - the noun otherwise; `singular` with an `s` appended by default
2900
+ */
2901
+ static readonly plural: (n: number, singular: string, plural?: string) => string;
2902
+ }
2903
+ //#endregion
2904
+ //#region src/GithubAnnotation.d.ts
2905
+ /**
2906
+ * The severity of a GitHub annotation.
2907
+ *
2908
+ * @public
2909
+ */
2910
+ type AnnotationLevel = "error" | "warning" | "notice";
2911
+ /**
2912
+ * What an annotation says about where it points and what it is called.
2913
+ *
2914
+ * @public
2915
+ */
2916
+ interface GithubAnnotationProperties {
2917
+ /** `error`, `warning` or `notice`. */
2918
+ readonly level: AnnotationLevel;
2919
+ /** The repository-relative path of the annotated file. */
2920
+ readonly file?: string;
2921
+ /** The first annotated line, 1-based. */
2922
+ readonly line?: number;
2923
+ /** The first annotated column, 1-based. */
2924
+ readonly col?: number;
2925
+ /** The last annotated line. */
2926
+ readonly endLine?: number;
2927
+ /** The last annotated column. */
2928
+ readonly endColumn?: number;
2929
+ /** A short title shown above the annotation. */
2930
+ readonly title?: string;
2931
+ }
2932
+ /**
2933
+ * GitHub Actions annotations, as workflow commands.
2934
+ *
2935
+ * @public
2936
+ */
2937
+ export declare class GithubAnnotation {
2938
+ private constructor();
2939
+ /**
2940
+ * Format an annotation as a workflow command: `::error title=T,file=F,line=1,endLine=2,col=3,endColumn=4::message`.
2941
+ *
2942
+ * @remarks
2943
+ * The message escapes `%`, CR and LF; a property value escapes those and `:` and `,`, per GitHub's
2944
+ * [workflow-command documentation](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions).
2945
+ * The percent sign is escaped first, so an escape that was just written is never escaped again. An unescaped line
2946
+ * break in a message would let the text after it be read as a new command, which is why the escaping is not optional.
2947
+ *
2948
+ * A property that is not given is left out, and the properties are written in the order `title`, `file`, `line`,
2949
+ * `endLine`, `col`, `endColumn`, the same as `@effected/github-commands`' `WorkflowCommand`, which this renders
2950
+ * through.
2951
+ *
2952
+ * @param annotation - the level and the optional file, position and title
2953
+ * @param message - the annotation's text
2954
+ */
2955
+ static readonly format: (annotation: GithubAnnotationProperties, message: string) => string;
2956
+ }
2957
+ //#endregion
2958
+ //#region src/NotInteractive.d.ts
2959
+ declare const NotInteractive_base: Schema.Class<NotInteractive, Schema.TaggedStruct<"NotInteractive", {}>, import("effect/Cause").YieldableError>;
2960
+ /**
2961
+ * A command needed to prompt, but there is no terminal to prompt on.
2962
+ *
2963
+ * @remarks
2964
+ * Exits `64` (BSD `EX_USAGE`) through core's `Runtime.errorExitCode` marker:
2965
+ * the caller invoked the command the wrong way, so the fix is to run it in a
2966
+ * terminal or pass the flag that supplies the answer. Its default rendering is
2967
+ * one line, `not interactive: run in a terminal or pass the flag`, which is the error's `message`, so a consumer
2968
+ * `render` can print `error.message` and keep it.
2969
+ *
2970
+ * @public
2971
+ */
2972
+ export declare class NotInteractive extends NotInteractive_base {
2973
+ /**
2974
+ * The one line, `not interactive: run in a terminal or pass the flag`.
2975
+ *
2976
+ * @remarks
2977
+ * A prototype getter, not a field, so it is not part of the encoded form, equality or a JSON dump. Assigning to
2978
+ * it is ignored: a library that rewrites `error.message` must not make this error throw, which a getter-only
2979
+ * property does in strict mode. The line is fixed.
2980
+ */
2981
+ get message(): string;
2982
+ set message(_value: string);
2983
+ /**
2984
+ * The process exit code: `64`.
2985
+ *
2986
+ * @remarks
2987
+ * A prototype getter rather than an own field, so a JSON or logger dump of the error does not carry the
2988
+ * runtime marker. It is the error's own code, so `CliRuntime`'s `usageExitCode` option does not change it.
2989
+ */
2990
+ get [Runtime.errorExitCode](): number;
2991
+ }
2992
+ //#endregion
2993
+ //#region src/Render.d.ts
2994
+ /**
2995
+ * Everything a renderer needs to know about where its output is going.
2996
+ *
2997
+ * @remarks
2998
+ * A renderer is a pure function of a document and one of these, so two contexts give two renderings of the
2999
+ * same document. `paint` and `link` are plain functions: a context for a stream with no colour passes the
3000
+ * identity, and one whose hyperlinks are off passes a `link` that returns its label unchanged.
3001
+ *
3002
+ * @public
3003
+ */
3004
+ interface RenderContext {
3005
+ /**
3006
+ * The display columns available.
3007
+ *
3008
+ * @remarks
3009
+ * `Infinity` is no limit. A renderer clamps what it is given: zero or a negative width is 1, and `NaN` is 80.
3010
+ * `Render.context` gives a human `TerminalEnv.width()`, which is the terminal's columns as stdout reports them, even
3011
+ * for a context built for `"stderr"` (core's `Terminal` has one width); pass `width` to override it.
3012
+ */
3013
+ readonly width: number;
3014
+ /** Who the output is for. */
3015
+ readonly audience: AudienceKind;
3016
+ /** The colour level of the stream being written. */
3017
+ readonly color: ColorLevel;
3018
+ /** Paints text in a token or a style; the identity at colour `none`. */
3019
+ readonly paint: (token: TokenName | Style, text: string) => string;
3020
+ /** The glyph set: status glyphs, separators, the ellipsis. */
3021
+ readonly glyphs: GlyphSet;
3022
+ /**
3023
+ * Wraps a label as a link to a target; returns the label unchanged when links are off.
3024
+ *
3025
+ * @remarks
3026
+ * It must be pure and cheap: a renderer may call it more than once for one link. `Render.ansi` calls it once with
3027
+ * the plain label to learn whether links are on (an unchanged label means off, and the target is then written
3028
+ * after the label), and again when it paints, with the painted label. So it must decide on whether links are
3029
+ * allowed, not on the label's content, and it must not count or record its calls.
3030
+ */
3031
+ readonly link: (target: LinkTarget, label: string) => string;
3032
+ /** Turns an absolute path into its display form; the identity by default. */
3033
+ readonly displayPath: (absolute: string) => string;
3034
+ /**
3035
+ * Whether the output will be read by the GitHub Actions runner, which has two command parsers: a line is a command
3036
+ * if, after .NET whitespace, it starts with `::`, or if `##[` occurs ANYWHERE in it (a bare `##` is not one). When
3037
+ * `true`, `plain`, `ansi` and `markdown` put a zero-width space in front of such a `::` line and between `##` and
3038
+ * `[` at each `##[`, so a document's text, an error message, say, can never inject a command. Unset or `false`
3039
+ * leaves their text alone. `Render.githubLog` ignores it and always neutralizes: its output is for the runner by
3040
+ * definition.
3041
+ *
3042
+ * @remarks
3043
+ * The trigger is the runner, not the audience: a person or an agent whose output lands in an Actions log is read
3044
+ * by it just the same. `Render.context` sets it when `CurrentRuntimeEnv` says GitHub Actions.
3045
+ */
3046
+ readonly neutralizeWorkflowCommands?: boolean | undefined;
3047
+ /**
3048
+ * A base URL for file links in markdown, such as `https://github.com/<owner>/<repo>/blob/<sha>/`. When set,
3049
+ * `Render.markdown` links a `{ file }` target to the base followed by its display path (`displayPath`, URL-encoded,
3050
+ * without a leading `/`) and `#L<line>` when it has a line, in place of a `file://` URL a step summary's reader
3051
+ * cannot open. The other renderers do not read it.
3052
+ */
3053
+ readonly linkBase?: string | undefined;
3054
+ }
3055
+ /**
3056
+ * Options for {@link Render.contextOf}.
3057
+ *
3058
+ * @public
3059
+ */
3060
+ interface RenderContextOfOptions {
3061
+ /** Who the output is for. An `agent` gets no escape of any kind, whatever the other options say. */
3062
+ readonly audience: AudienceKind;
3063
+ /** The colour level; `none` by default, which paints nothing. */
3064
+ readonly color?: ColorLevel | undefined;
3065
+ /** The glyph set; Unicode by default. */
3066
+ readonly glyphs?: GlyphSet | undefined;
3067
+ /** The display columns; unbounded (`Infinity`) by default. */
3068
+ readonly width?: number | undefined;
3069
+ /** Turns an absolute path into its display form; the identity by default. */
3070
+ readonly displayPath?: ((absolute: string) => string) | undefined;
3071
+ /**
3072
+ * Hyperlinks: `off` (the default) leaves every label unlinked; a `CliLinksShape`, such as a `CliLinks` service's
3073
+ * value, makes OSC 8 hyperlinks through {@link CliLinks.linker}, for any audience but an agent.
3074
+ */
3075
+ readonly links?: "off" | CliLinksShape | undefined;
3076
+ /**
3077
+ * See {@link RenderContext.neutralizeWorkflowCommands}. `true` by default for a `ci` audience, whose output the
3078
+ * Actions runner may read (it is harmless elsewhere), and unset for the others; an explicit `false` always wins.
3079
+ */
3080
+ readonly neutralizeWorkflowCommands?: boolean | undefined;
3081
+ /** See {@link RenderContext.linkBase}; unset by default. */
3082
+ readonly linkBase?: string | undefined;
3083
+ }
3084
+ /**
3085
+ * Options for {@link Render.context}.
3086
+ *
3087
+ * @public
3088
+ */
3089
+ interface RenderContextOptions {
3090
+ /**
3091
+ * The display columns to lay out at. By default a human gets `TerminalEnv.width()`, the terminal's columns as
3092
+ * stdout reports them even when the stream is `"stderr"`, and an agent or a CI gets no limit at all.
3093
+ */
3094
+ readonly width?: number | undefined;
3095
+ /** Turns an absolute path into its display form, for example relative to the working directory; the identity by default. */
3096
+ readonly displayPath?: ((absolute: string) => string) | undefined;
3097
+ }
3098
+ /**
3099
+ * Pure renderers of a document: `(doc, context) => string`.
3100
+ *
3101
+ * @remarks
3102
+ * A renderer has no environment and no effects, so the same document and context always give the same string.
3103
+ * {@link Render.plain} is for agents; the rest of the set follows.
3104
+ *
3105
+ * @example
3106
+ * ```ts
3107
+ * import { Doc, Render } from "@effected/cli"
3108
+ *
3109
+ * const doc = [Doc.heading(2, "Results"), Doc.paragraph("3 checks passed")]
3110
+ * const ctx = Render.contextOf({ audience: "agent" })
3111
+ *
3112
+ * Render.plain(doc, ctx)
3113
+ * // => "Results\n3 checks passed"
3114
+ * ```
3115
+ *
3116
+ * @public
3117
+ */
3118
+ export declare class Render {
3119
+ private constructor();
3120
+ /**
3121
+ * The {@link RenderContext} for a stream, from the services a CLI already has.
3122
+ *
3123
+ * @remarks
3124
+ * Everything is read once, here, so the renderers stay pure:
3125
+ *
3126
+ * - `audience` is the `Audience` in force, so an audience flag is honoured;
3127
+ * - `color`, `paint` and `glyphs` are the `CliTheme`'s for THAT stream, so redirecting stdout does not quiet
3128
+ * stderr; except that an agent's `color` is `none` and its `paint` the identity, so no renderer, `ansi` included,
3129
+ * writes an escape for an agent whatever the terminal could do;
3130
+ * - `link` is `CliLinks.linker` over that stream's hyperlink support and the audience, so an agent never
3131
+ * gets an escape and a terminal without OSC 8 gets the label;
3132
+ * - `neutralizeWorkflowCommands` is set when `CurrentRuntimeEnv` says GitHub Actions (read if present, not
3133
+ * required), for every audience, since the runner reads whatever is written there;
3134
+ * - `width` is the option, else `TerminalEnv.width()` for a human, and **unbounded** (`Infinity`) for an agent
3135
+ * or a CI, so nothing a reader needs is truncated or wrapped for a terminal that is not there.
3136
+ *
3137
+ * @param stream - the stream the output is for
3138
+ * @param options - an explicit width and a path display function
3139
+ */
3140
+ static readonly context: (stream: "stdout" | "stderr", options?: RenderContextOptions) => Effect.Effect<RenderContext, never, CliTheme | TerminalEnv | Audience | CliLinks>;
3141
+ /**
3142
+ * A {@link RenderContext} from plain options, for a caller outside Effect, such as a test reporter or an Ink tree.
3143
+ *
3144
+ * @remarks
3145
+ * Pure: nothing is read from the environment. The defaults are colour `none`, the identity paint, no links,
3146
+ * Unicode glyphs, unbounded width and the identity `displayPath`, so a context built from an audience alone renders
3147
+ * with no escape of any kind. A colour level paints with the default token styles. An `agent` is colourless and
3148
+ * unlinked whatever `color` and `links` say, as in {@link Render.context}.
3149
+ *
3150
+ * @param options - the audience, and the colour, glyphs, width, path display, links, neutralizing and link base
3151
+ */
3152
+ static readonly contextOf: (options: RenderContextOfOptions) => RenderContext;
3153
+ /**
3154
+ * Render a document as plain text for an agent.
3155
+ *
3156
+ * @remarks
3157
+ * There are no escape sequences of any kind, whatever the context's colour or hyperlinks allow: `paint` and
3158
+ * `link` are never called, and a control character in a document's text is removed. The audience is treated as
3159
+ * `agent`, so a path joins with ` > `.
3160
+ *
3161
+ * - A heading is its text alone, code is in backticks, and a link is its label followed by the target in
3162
+ * parentheses, as `path:line:col` through `displayPath` for a file, unless the label already is the target.
3163
+ * - A paragraph wraps to the width and is never truncated; a word longer than the width, such as a URL, stays
3164
+ * whole on its own line.
3165
+ * - A list uses `- ` items, and past its cap the overflow row. A table is aligned text columns with a rule under
3166
+ * the header; a short row is padded with empty cells, a cell holding line breaks shows its first line and an
3167
+ * ellipsis, and cells are truncated only when the table is wider than the context, widest column first. A tree uses the glyph set's tree segments.
3168
+ * - A collapsible is its title and the indented body, a callout its upper-case kind and the body, a code block
3169
+ * four-space indented, and a diff `- expected` lines then `+ received` lines, the cap limiting each side.
3170
+ * - Counts take their total and their visible counters from {@link Doc.total} and {@link Doc.visibleCounters}.
3171
+ * Inline gives `3/5 passed, 1 failed (1.2s)`: the first counter is the headline and shows its share of the
3172
+ * total, unless `share` is `false`. Columns gives aligned label and number pairs, and row one line of cells.
3173
+ * - A link with `suffix: false` never has its target after the label, and one with `suffix: true` always does.
3174
+ * - Verbatim text is its lines exactly, each indented by `indent` spaces, never wrapped; an annotation is nothing.
3175
+ * - Strong and emphasised content is its text; a file is its display path, unlinked. `Lines` are one line per entry,
3176
+ * a `Line` with `truncate` is cut to the width with the ellipsis, and diff text is its lines as given, the cap
3177
+ * followed by `… N more lines`. A counts table is a table with a column per counter key and the total row last,
3178
+ * and a counts `suffix` follows the duration.
3179
+ * - A compact list has no blank lines inside an item. A `style: "pipe"` table is istanbul's shape: a rule of dashes
3180
+ * meeting at `|` above and below the header and at the end, and cells joined with ` | `.
3181
+ * - Top-level blocks are consecutive lines; a section separates its title and children with blank lines.
3182
+ *
3183
+ * @param doc - the document
3184
+ * @param ctx - where the output is going
3185
+ */
3186
+ static readonly plain: (doc: Document, ctx: RenderContext) => string;
3187
+ /**
3188
+ * Render a document for a person: the same layout as {@link Render.plain}, painted and linked.
3189
+ *
3190
+ * @remarks
3191
+ * The context's `paint` and `link` do the styling, so a context at colour `none` with links off gives exactly
3192
+ * what `plain` gives, apart from two things: code has no backticks (it is painted `accent` instead), and a path
3193
+ * joins with the audience's separator (`›` for a person) rather than ` > `. Tokens:
3194
+ *
3195
+ * - headings, section and collapsible titles, and table headers are `emphasis`; the rule under a header, tree
3196
+ * lines and overflow rows are `muted`;
3197
+ * - a status glyph takes its definition's token, and a diff's `-` lines are `failure` and `+` lines `success`;
3198
+ * - a callout's label takes its kind's token (`note` info, `tip` success, `important` accent, `warning` warning,
3199
+ * `caution` error), and a counter the token of its status, with the qualifier, the duration and the suffix `muted`.
3200
+ * A `Counts` with `paint: "none"` paints none of it, and with `paint: "glyph"` only a status glyph.
3201
+ * - strong content is bold and emphasised content italic, over any token it has; in diff text a `+` line is
3202
+ * `success` and a `-` line `failure`; a counts table's counts take their status's token.
3203
+ *
3204
+ * A link goes through `ctx.link`, which makes an OSC 8 hyperlink only when the policy allows it. When it does
3205
+ * not (it returns the label unchanged), the target follows the label in parentheses, muted, as in plain text.
3206
+ *
3207
+ * Text is cut and wrapped before it is painted, so a colour or a hyperlink is never cut in half, and a table
3208
+ * cut to the width keeps the colour of what remains. Tables are plain aligned columns, never box drawing:
3209
+ * box drawing costs two columns of every row for nothing a rule and the padding do not already say, and it
3210
+ * cannot be matched to plain text.
3211
+ *
3212
+ * @param doc - the document
3213
+ * @param ctx - where the output is going
3214
+ */
3215
+ static readonly ansi: (doc: Document, ctx: RenderContext) => string;
3216
+ /**
3217
+ * Render a document as GitHub-flavoured markdown, for a step summary or a file.
3218
+ *
3219
+ * @remarks
3220
+ * There is no ANSI and no OSC 8 (`paint` and `link` are never called), and the width does not apply: a reader
3221
+ * wraps. Everything a document carries as text is escaped so that it cannot become markdown: the characters
3222
+ * that mean something, `|` everywhere so text can never form a table, the marker at the start of a line (a
3223
+ * heading, bullet, setext underline or ordered item) and the start of an autolink (a URL scheme or `www.`). An
3224
+ * email address is not escaped: a reader may make a `mailto:` link of it, which is harmless. A leading indent is
3225
+ * dropped, since markdown would read it as code.
3226
+ *
3227
+ * Under GitHub Actions (`neutralizeWorkflowCommands`) the same neutralizing applies, since markdown can be printed
3228
+ * to the log. Markdown escapes `[` in text, so `##[` cannot appear outside code and the headings are untouched
3229
+ * (a bare `##` is not a command); code spans and blocks, which are not escaped, get the zero-width space, which can
3230
+ * also land inside code or table text where it would otherwise have formed a command.
3231
+ *
3232
+ * GitHub also turns `@user`, `@org/team`, `#123` and commit SHAs in rendered markdown into mentions and references.
3233
+ * Nothing here escapes them: in a step summary they do not notify, but markdown posted as a comment could ping
3234
+ * whoever the text names.
3235
+ *
3236
+ * - A heading is `#` repeated by its level. A section's title is a heading of level 2 for a section at the top,
3237
+ * one deeper for each section nested inside it, to level 6.
3238
+ * - A table is a GFM pipe table: `|` is `\|` in a cell and a line break in a cell is `<br>`. A short row is
3239
+ * padded and a long one widens the table. With no header, the header row is empty.
3240
+ * - A collapsible is `<details><summary>title</summary>`, a blank line, the body as markdown, a blank line and
3241
+ * `</details>`. The title is HTML, so it is HTML-escaped and plain.
3242
+ * - A callout is a quoted `[!KIND]` followed by its body. A code block is a fence longer than any backtick run it
3243
+ * holds, and a diff a `diff` fence of `-` and `+` lines.
3244
+ * - A link is `[label](url)` when it has a URL a reader can follow: an `http`, `https`, `mailto`, `file` or
3245
+ * `vscode` URL, or a relative one. A file link has one when its path is absolute (`file://`). Otherwise, such as
3246
+ * for a `javascript:` URL or a relative file path, it is the label followed by the target in inline code, as
3247
+ * `path:line:col` for a file.
3248
+ * - A list is bullets, a tree a nested bullet list under its root label, and overflow rows paragraphs after what
3249
+ * they cap. Counts inline is a paragraph, columns a list of `label: n` and row a one-row table of the counter
3250
+ * labels over their numbers, every header named (the duration as `duration`), with the label above the table and
3251
+ * the qualifier and suffix below it.
3252
+ * - Verbatim text is a fenced code block, so its indentation survives; an annotation is nothing.
3253
+ * - Strong content is `**…**` and emphasised `*…*`, which GFM reads inside a word too, with the spaces at a run's
3254
+ * edges kept outside the markers. `Lines` are one paragraph with a hard break between entries, an empty entry
3255
+ * between two others an empty line (one at either end is dropped); a `Line` is one
3256
+ * paragraph; diff text a `diff` fence; a counts table a pipe table; a file is its display path as text.
3257
+ * - With `linkBase`, a file link goes to the base and the display path, plus `#L<line>`, in place of `file://`. A
3258
+ * display path that is absolute or climbs out with `..` is not under the base, so its link has no URL form.
3259
+ * - A compact list item joins its parts with no blank line, putting a hard break where two paragraphs would merge.
3260
+ * A row of counts names its duration column `duration`.
3261
+ *
3262
+ * @param doc - the document
3263
+ * @param ctx - where the output is going; its glyph set, audience and `displayPath` are used
3264
+ */
3265
+ static readonly markdown: (doc: Document, ctx: RenderContext) => string;
3266
+ /**
3267
+ * Render a document for a GitHub Actions log.
3268
+ *
3269
+ * @remarks
3270
+ * Everything is what {@link Render.plain} renders, except an annotation, which is one workflow command
3271
+ * (`::error file=…,line=…::message`), and a collapsible that starts a line, which is a group: `::group::title`, its
3272
+ * body, `::endgroup::`. An annotation is a command at the top level, as a top-level section's child, and as a direct
3273
+ * child of a group's body; anywhere deeper (inside a list, a callout, or a section within a group) it is nothing,
3274
+ * as in plain. Its message and properties are escaped, so no text can end
3275
+ * the command or start another, and the kit's own command is never neutralized. That is a top-level collapsible, or one that is a direct child of a
3276
+ * top-level section. GitHub does not nest groups, so a collapsible inside a group, or inside a list or callout
3277
+ * (where it would not start a line), keeps plain's rendering: its title on a line and its body indented.
3278
+ *
3279
+ * The runner has two command parsers, and a line is a command if either accepts it: after its leading whitespace it
3280
+ * starts with `::`, or `##[` occurs ANYWHERE in it (a bare `##` is not one). A document's text must not be able to
3281
+ * do that (`::add-mask::`, `::error::`, `##[error]`), so such a `::` line gets a zero-width space in front, which
3282
+ * the runner does not treat as whitespace, and every `##[` gets one between the `##` and the `[`. The text is
3283
+ * otherwise unchanged. A
3284
+ * group's title is a command's data, so its `%`, CR and LF are escaped. Lines are split at CR, LF and CRLF before
3285
+ * that check, as the runner splits them. There is no ANSI and `paint` and `link` are never called, and the audience
3286
+ * is treated as `agent`, as `plain` does.
3287
+ *
3288
+ * @param doc - the document
3289
+ * @param ctx - where the output is going; the width, glyph set and `displayPath` are used as plain uses them
3290
+ */
3291
+ static readonly githubLog: (doc: Document, ctx: RenderContext) => string;
3292
+ }
3293
+ //#endregion
539
3294
  //#region src/SchemaIssueRenderer.d.ts
540
3295
  /**
541
- * Turn a `SchemaIssue` tree into lines a user can act on.
3296
+ * Turns a `SchemaIssue` tree into lines a user can act on.
542
3297
  *
543
3298
  * @remarks
544
3299
  * A decode failure arrives as a structured tree; a person needs
545
- * `unknown key at groups.g.cleanup.rulesetz`.
3300
+ * `unknown key at groups.g.cleanup.rulesetz`. The formatters core ships for
3301
+ * this live on `SchemaIssue` rather than on `SchemaError` or `Schema`, and
3302
+ * `SchemaError.message` does not use them, so printing the error alone does not
3303
+ * give these lines.
546
3304
  *
547
- * **Core already ships the formatters this wraps, and they are effectively
548
- * undiscoverable.** They live on `SchemaIssue` rather than `SchemaError` or
549
- * `Schema`, they are named `makeFormatter*` rather than anything containing
550
- * "render" or "format issue", and `SchemaError.message` does not use them — so
551
- * the obvious probe, printing the error, hints at nothing. Two engineers
552
- * searched for two rounds and concluded core had none. This export exists to
553
- * end that search, and the one phrasing override is a bonus rather than the
554
- * point.
3305
+ * The lines and `CliFailure`'s tree are two views of the same rejected values (`internal/format`), so a schema
3306
+ * failure in the default report and these lines never disagree.
555
3307
  *
556
3308
  * @example
557
3309
  * ```ts
@@ -583,5 +3335,5 @@ export declare class SchemaIssueRenderer {
583
3335
  static readonly render: (issue: unknown) => ReadonlyArray<string>;
584
3336
  }
585
3337
  //#endregion
586
- export type { CliExitShape, CliLoggerOptions, FailureDetails, MainOptions, ReportFailuresOptions };
3338
+ export type { AnnotationLevel, AnnotationOptions, AudienceFlagInput, Block, BlockOf, CliAudienceFlagsOptions, CliDocSource, CliEnvOptions, CliEnvServices, CliEnvTestOptions, CliEnvTestServices, CliExitShape, CliFailureOptions, CliLinksLinkerOptions, CliLinksOptions, CliLinksShape, CliLogFile, CliLogFileOptions, CliLogOptions, CliLoggerOptions, CliMessageOptions, CliPromptFallbackOptions, CliPromptTarget, CliThemeOptions, CliThemeShape, CliThemeTestOptions, Column, CoreStatusName, Counter, CountsOptions, CountsRow, CountsTableOptions, DocPrintOptions, Document, EditorLinks, FailureDetails, GithubAnnotationProperties, GlyphSelectOptions, GlyphSet, Inline, InlineInput, InlineOf, LinkOptions, LinkTarget, ListOptions, MainOptions, NamedColor, OverflowOptions, PercentOptions, RenderContext, RenderContextOfOptions, RenderContextOptions, ReportFailuresOptions, RequiresAudienceFlags, StatusDef, StatusRef, StreamTheme, Style, TableOptions, TokenName, TreeInput, TreeNode, TruncateOptions };
587
3339
  //# sourceMappingURL=index.d.ts.map