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