1/**
2 * Structured help documentation model for the unstable CLI package. A
3 * `HelpDoc` value captures the user-facing parts of a command, including its
4 * description, usage string, positional arguments, flags, global flags,
5 * subcommands, annotations, and examples.
6 *
7 * This module only defines the data shapes used to describe help. Rendering
8 * that data as terminal text is handled by `CliOutput`.
9 *
10 * @stability unstable
11 * @since 4.0.0
12 */
13
14import type { NonEmptyReadonlyArray } from "../Array.ts"
15import type * as Context from "../Context.ts"
16import type * as Option from "../Option.ts"
17
18/**
19 * Structured representation of help documentation for a command.
20 * This data structure is independent of formatting, allowing for
21 * different output formats (text, markdown, JSON, etc.).
22 *
23 * **Example** (Defining command help documentation)
24 *
25 * ```ts import.meta.vitest
26 * import { Context, Option as O } from "effect"
27 * import type { HelpDoc } from "effect/cli"
28 *
29 * const deployCommandHelp: HelpDoc.HelpDoc = {
30 * description: "Deploy your application to the cloud",
31 * usage: "myapp deploy [options] <target>",
32 * annotations: Context.empty(),
33 * flags: [
34 * {
35 * name: "verbose",
36 * aliases: ["-v"],
37 * type: "boolean",
38 * description: O.some("Enable verbose logging"),
39 * required: false
40 * },
41 * {
42 * name: "env",
43 * aliases: ["-e"],
44 * type: "string",
45 * description: O.some("Target environment"),
46 * required: true
47 * }
48 * ],
49 * args: [
50 * {
51 * name: "target",
52 * type: "string",
53 * description: O.some("Deployment target (e.g., 'production', 'staging')"),
54 * required: true,
55 * variadic: false
56 * }
57 * ]
58 * }
59 *
60 * deployCommandHelp.usage // => "myapp deploy [options] <target>"
61 * ```
62 *
63 * @stability unstable
64 * @category models
65 * @since 4.0.0
66 */
67export interface HelpDoc {
68 /**
69 * Brief description of what the command does
70 */
71 readonly description: string
72
73 /**
74 * Usage syntax showing how to invoke the command
75 * Example: "myapp deploy [flags]"
76 */
77 readonly usage: string
78
79 /**
80 * List of available flags/options for this command
81 */
82 readonly flags: ReadonlyArray<FlagDoc>
83
84 /**
85 * Global flags available to all commands (e.g., --help, --version).
86 */
87 readonly globalFlags?: ReadonlyArray<FlagDoc>
88
89 /**
90 * Custom command annotations.
91 */
92 readonly annotations: Context.Context<never>
93
94 /**
95 * List of positional arguments for this command
96 */
97 readonly args?: ReadonlyArray<ArgDoc>
98
99 /**
100 * Optional list of subcommands if this is a parent command
101 */
102 readonly subcommands?: ReadonlyArray<SubcommandGroupDoc>
103
104 /**
105 * Optional concrete usage examples for the command
106 */
107 readonly examples?: ReadonlyArray<ExampleDoc>
108}
109
110/**
111 * Documentation for a command usage example
112 *
113 * @stability unstable
114 * @category models
115 * @since 4.0.0
116 */
117export interface ExampleDoc {
118 /**
119 * Command line invocation example
120 */
121 readonly command: string
122
123 /**
124 * Optional explanation for the example
125 */
126 readonly description?: string | undefined
127}
128
129/**
130 * Documentation for a single command-line flag/option
131 *
132 * **Example** (Documenting command flags)
133 *
134 * ```ts import.meta.vitest
135 * import { Option as O } from "effect"
136 * import type { HelpDoc } from "effect/cli"
137 *
138 * const verboseFlag: HelpDoc.FlagDoc = {
139 * name: "verbose",
140 * aliases: ["-v", "--verbose"],
141 * type: "boolean",
142 * description: O.some("Enable verbose output"),
143 * required: false
144 * }
145 *
146 * const portFlag: HelpDoc.FlagDoc = {
147 * name: "port",
148 * aliases: ["-p"],
149 * type: "integer",
150 * description: O.some("Port number to use"),
151 * required: true
152 * }
153 *
154 * const names = [verboseFlag.name, portFlag.name] // => ["verbose", "port"]
155 * ```
156 *
157 * @stability unstable
158 * @category models
159 * @since 4.0.0
160 */
161export interface FlagDoc {
162 /**
163 * Primary name of the flag (e.g., "verbose")
164 */
165 readonly name: string
166
167 /**
168 * Alternative names/aliases for the flag (e.g., ["-v"])
169 */
170 readonly aliases: ReadonlyArray<string>
171
172 /**
173 * Type of the flag value (e.g., "string", "boolean", "integer")
174 */
175 readonly type: string
176
177 /**
178 * Description of what the flag does
179 */
180 readonly description: Option.Option<string>
181
182 /**
183 * Whether this flag is required
184 */
185 readonly required: boolean
186}
187
188/**
189 * Documentation for a subcommand
190 *
191 * **Example** (Documenting subcommands)
192 *
193 * ```ts import.meta.vitest
194 * import { Context, Option as O } from "effect"
195 * import type { HelpDoc } from "effect/cli"
196 *
197 * const deploySubcommand: HelpDoc.SubcommandDoc = {
198 * name: "deploy",
199 * alias: "d",
200 * shortDescription: "Deploy app",
201 * description: "Deploy the application to the cloud"
202 * }
203 *
204 * const buildSubcommand: HelpDoc.SubcommandDoc = {
205 * name: "build",
206 * alias: undefined,
207 * shortDescription: undefined,
208 * description: "Build the application for production"
209 * }
210 *
211 * // Used in parent command's help documentation
212 * const mainCommandHelp: HelpDoc.HelpDoc = {
213 * description: "Cloud deployment tool",
214 * usage: "myapp <command> [options]",
215 * annotations: Context.empty(),
216 * flags: [],
217 * subcommands: [{
218 * group: undefined,
219 * commands: [deploySubcommand, buildSubcommand]
220 * }]
221 * }
222 *
223 * mainCommandHelp.subcommands?.[0].commands.map((command) => command.name) // => ["deploy", "build"]
224 * ```
225 *
226 * @stability unstable
227 * @category models
228 * @since 4.0.0
229 */
230export interface SubcommandDoc {
231 /**
232 * Name of the subcommand
233 */
234 readonly name: string
235
236 /**
237 * Optional short alias for invoking the subcommand.
238 */
239 readonly alias: string | undefined
240
241 /**
242 * Optional short description of what the subcommand does.
243 */
244 readonly shortDescription: string | undefined
245
246 /**
247 * Brief description of what the subcommand does
248 */
249 readonly description: string
250}
251
252/**
253 * Documentation for a grouped subcommand listing
254 *
255 * @stability unstable
256 * @category models
257 * @since 4.0.0
258 */
259export interface SubcommandGroupDoc {
260 /**
261 * Group name used in help output.
262 * Undefined means the default ungrouped section.
263 */
264 readonly group: string | undefined
265
266 /**
267 * Subcommands in this group.
268 */
269 readonly commands: NonEmptyReadonlyArray<SubcommandDoc>
270}
271
272/**
273 * Documentation for a positional argument
274 *
275 * **Example** (Documenting positional arguments)
276 *
277 * ```ts import.meta.vitest
278 * import { Context, Option as O } from "effect"
279 * import type { HelpDoc } from "effect/cli"
280 *
281 * const sourceArg: HelpDoc.ArgDoc = {
282 * name: "source",
283 * type: "file",
284 * description: O.some("Source file to process"),
285 * required: true,
286 * variadic: false
287 * }
288 *
289 * const filesArg: HelpDoc.ArgDoc = {
290 * name: "files",
291 * type: "file",
292 * description: O.some("Files to process (can specify multiple)"),
293 * required: false,
294 * variadic: true
295 * }
296 *
297 * // Used in command help documentation
298 * const copyCommandHelp: HelpDoc.HelpDoc = {
299 * description: "Copy files from source to destination",
300 * usage: "copy <source> [files...]",
301 * annotations: Context.empty(),
302 * flags: [],
303 * args: [sourceArg, filesArg]
304 * }
305 *
306 * copyCommandHelp.args?.map((arg) => arg.name) // => ["source", "files"]
307 * ```
308 *
309 * @stability unstable
310 * @category models
311 * @since 4.0.0
312 */
313export interface ArgDoc {
314 /**
315 * Name of the argument (e.g., "source", "destination")
316 */
317 readonly name: string
318
319 /**
320 * Type of the argument value (e.g., "string", "file", "directory")
321 */
322 readonly type: string
323
324 /**
325 * Description of what the argument is for
326 */
327 readonly description: Option.Option<string>
328
329 /**
330 * Whether this argument is required or optional
331 */
332 readonly required: boolean
333
334 /**
335 * Whether this argument is variadic (accepts multiple values)
336 */
337 readonly variadic: boolean
338}