automate.ax 0.1.3

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 (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/automation/actions.d.ts +200 -0
  4. package/dist/automation/actions.d.ts.map +1 -0
  5. package/dist/automation/actions.js +286 -0
  6. package/dist/automation/automation.d.ts +16 -0
  7. package/dist/automation/automation.d.ts.map +1 -0
  8. package/dist/automation/automation.js +10 -0
  9. package/dist/automation/integrations.d.ts +63 -0
  10. package/dist/automation/integrations.d.ts.map +1 -0
  11. package/dist/automation/integrations.js +58 -0
  12. package/dist/automation/runtime.d.ts +73 -0
  13. package/dist/automation/runtime.d.ts.map +1 -0
  14. package/dist/automation/runtime.js +92 -0
  15. package/dist/automation/signal.d.ts +58 -0
  16. package/dist/automation/signal.d.ts.map +1 -0
  17. package/dist/automation/signal.js +80 -0
  18. package/dist/automation/subscribe.d.ts +10 -0
  19. package/dist/automation/subscribe.d.ts.map +1 -0
  20. package/dist/automation/subscribe.js +16 -0
  21. package/dist/cli/command-names.d.ts +46 -0
  22. package/dist/cli/command-names.d.ts.map +1 -0
  23. package/dist/cli/command-names.js +46 -0
  24. package/dist/cli/commands/accept.d.ts +20 -0
  25. package/dist/cli/commands/accept.d.ts.map +1 -0
  26. package/dist/cli/commands/accept.js +42 -0
  27. package/dist/cli/commands/debug.d.ts +4 -0
  28. package/dist/cli/commands/debug.d.ts.map +1 -0
  29. package/dist/cli/commands/debug.js +52 -0
  30. package/dist/cli/commands/deploy.d.ts +24 -0
  31. package/dist/cli/commands/deploy.d.ts.map +1 -0
  32. package/dist/cli/commands/deploy.js +292 -0
  33. package/dist/cli/commands/login.d.ts +20 -0
  34. package/dist/cli/commands/login.d.ts.map +1 -0
  35. package/dist/cli/commands/login.js +145 -0
  36. package/dist/cli/commands/logout.d.ts +4 -0
  37. package/dist/cli/commands/logout.d.ts.map +1 -0
  38. package/dist/cli/commands/logout.js +32 -0
  39. package/dist/cli/commands/me.d.ts +4 -0
  40. package/dist/cli/commands/me.d.ts.map +1 -0
  41. package/dist/cli/commands/me.js +48 -0
  42. package/dist/cli/commands/org-api-key-create.d.ts +26 -0
  43. package/dist/cli/commands/org-api-key-create.d.ts.map +1 -0
  44. package/dist/cli/commands/org-api-key-create.js +51 -0
  45. package/dist/cli/commands/org-api-key-delete.d.ts +33 -0
  46. package/dist/cli/commands/org-api-key-delete.d.ts.map +1 -0
  47. package/dist/cli/commands/org-api-key-delete.js +57 -0
  48. package/dist/cli/commands/org-api-key-list.d.ts +16 -0
  49. package/dist/cli/commands/org-api-key-list.d.ts.map +1 -0
  50. package/dist/cli/commands/org-api-key-list.js +58 -0
  51. package/dist/cli/commands/org-api-key-rename.d.ts +32 -0
  52. package/dist/cli/commands/org-api-key-rename.d.ts.map +1 -0
  53. package/dist/cli/commands/org-api-key-rename.js +54 -0
  54. package/dist/cli/commands/org-api-key.d.ts +2 -0
  55. package/dist/cli/commands/org-api-key.d.ts.map +1 -0
  56. package/dist/cli/commands/org-api-key.js +18 -0
  57. package/dist/cli/commands/org-billing.d.ts +16 -0
  58. package/dist/cli/commands/org-billing.d.ts.map +1 -0
  59. package/dist/cli/commands/org-billing.js +78 -0
  60. package/dist/cli/commands/org-create.d.ts +20 -0
  61. package/dist/cli/commands/org-create.d.ts.map +1 -0
  62. package/dist/cli/commands/org-create.js +64 -0
  63. package/dist/cli/commands/org-delete.d.ts +27 -0
  64. package/dist/cli/commands/org-delete.d.ts.map +1 -0
  65. package/dist/cli/commands/org-delete.js +53 -0
  66. package/dist/cli/commands/org-invite.d.ts +39 -0
  67. package/dist/cli/commands/org-invite.d.ts.map +1 -0
  68. package/dist/cli/commands/org-invite.js +66 -0
  69. package/dist/cli/commands/org-leave.d.ts +27 -0
  70. package/dist/cli/commands/org-leave.d.ts.map +1 -0
  71. package/dist/cli/commands/org-leave.js +56 -0
  72. package/dist/cli/commands/org-list.d.ts +4 -0
  73. package/dist/cli/commands/org-list.d.ts.map +1 -0
  74. package/dist/cli/commands/org-list.js +36 -0
  75. package/dist/cli/commands/org-remove.d.ts +33 -0
  76. package/dist/cli/commands/org-remove.d.ts.map +1 -0
  77. package/dist/cli/commands/org-remove.js +66 -0
  78. package/dist/cli/commands/org-rename.d.ts +26 -0
  79. package/dist/cli/commands/org-rename.d.ts.map +1 -0
  80. package/dist/cli/commands/org-rename.js +58 -0
  81. package/dist/cli/commands/org-show.d.ts +16 -0
  82. package/dist/cli/commands/org-show.d.ts.map +1 -0
  83. package/dist/cli/commands/org-show.js +48 -0
  84. package/dist/cli/commands/org-upgrade.d.ts +42 -0
  85. package/dist/cli/commands/org-upgrade.d.ts.map +1 -0
  86. package/dist/cli/commands/org-upgrade.js +171 -0
  87. package/dist/cli/commands/org.d.ts +2 -0
  88. package/dist/cli/commands/org.d.ts.map +1 -0
  89. package/dist/cli/commands/org.js +32 -0
  90. package/dist/cli/commands/project-create.d.ts +27 -0
  91. package/dist/cli/commands/project-create.d.ts.map +1 -0
  92. package/dist/cli/commands/project-create.js +61 -0
  93. package/dist/cli/commands/project-delete.d.ts +27 -0
  94. package/dist/cli/commands/project-delete.d.ts.map +1 -0
  95. package/dist/cli/commands/project-delete.js +55 -0
  96. package/dist/cli/commands/project-list.d.ts +17 -0
  97. package/dist/cli/commands/project-list.d.ts.map +1 -0
  98. package/dist/cli/commands/project-list.js +89 -0
  99. package/dist/cli/commands/project-rename.d.ts +26 -0
  100. package/dist/cli/commands/project-rename.d.ts.map +1 -0
  101. package/dist/cli/commands/project-rename.js +60 -0
  102. package/dist/cli/commands/project-show.d.ts +16 -0
  103. package/dist/cli/commands/project-show.d.ts.map +1 -0
  104. package/dist/cli/commands/project-show.js +37 -0
  105. package/dist/cli/commands/project.d.ts +2 -0
  106. package/dist/cli/commands/project.d.ts.map +1 -0
  107. package/dist/cli/commands/project.js +20 -0
  108. package/dist/cli/index.d.ts +19 -0
  109. package/dist/cli/index.d.ts.map +1 -0
  110. package/dist/cli/index.js +63 -0
  111. package/dist/cli/lib/api-client.d.ts +3 -0
  112. package/dist/cli/lib/api-client.d.ts.map +1 -0
  113. package/dist/cli/lib/api-client.js +14 -0
  114. package/dist/cli/lib/auth-client.d.ts +80 -0
  115. package/dist/cli/lib/auth-client.d.ts.map +1 -0
  116. package/dist/cli/lib/auth-client.js +61 -0
  117. package/dist/cli/lib/automation-build.d.ts +30 -0
  118. package/dist/cli/lib/automation-build.d.ts.map +1 -0
  119. package/dist/cli/lib/automation-build.js +90 -0
  120. package/dist/cli/lib/config-dir.d.ts +47 -0
  121. package/dist/cli/lib/config-dir.d.ts.map +1 -0
  122. package/dist/cli/lib/config-dir.js +146 -0
  123. package/dist/cli/lib/detail-block.d.ts +16 -0
  124. package/dist/cli/lib/detail-block.d.ts.map +1 -0
  125. package/dist/cli/lib/detail-block.js +25 -0
  126. package/dist/cli/lib/env.d.ts +4 -0
  127. package/dist/cli/lib/env.d.ts.map +1 -0
  128. package/dist/cli/lib/env.js +7 -0
  129. package/dist/cli/lib/hijack-clack-logger.d.ts +2 -0
  130. package/dist/cli/lib/hijack-clack-logger.d.ts.map +1 -0
  131. package/dist/cli/lib/hijack-clack-logger.js +8 -0
  132. package/dist/cli/lib/io.d.ts +19 -0
  133. package/dist/cli/lib/io.d.ts.map +1 -0
  134. package/dist/cli/lib/io.js +87 -0
  135. package/dist/cli/lib/json-output.d.ts +9 -0
  136. package/dist/cli/lib/json-output.d.ts.map +1 -0
  137. package/dist/cli/lib/json-output.js +12 -0
  138. package/dist/cli/lib/organization-detail-block.d.ts +30 -0
  139. package/dist/cli/lib/organization-detail-block.d.ts.map +1 -0
  140. package/dist/cli/lib/organization-detail-block.js +72 -0
  141. package/dist/cli/lib/project-config.d.ts +10 -0
  142. package/dist/cli/lib/project-config.d.ts.map +1 -0
  143. package/dist/cli/lib/project-config.js +21 -0
  144. package/dist/cli/lib/spinner.d.ts +18 -0
  145. package/dist/cli/lib/spinner.d.ts.map +1 -0
  146. package/dist/cli/lib/spinner.js +111 -0
  147. package/dist/cli/usage.d.ts +9 -0
  148. package/dist/cli/usage.d.ts.map +1 -0
  149. package/dist/cli/usage.js +468 -0
  150. package/dist/cli/utils.d.ts +291 -0
  151. package/dist/cli/utils.d.ts.map +1 -0
  152. package/dist/cli/utils.js +561 -0
  153. package/dist/cli.d.ts +3 -0
  154. package/dist/cli.d.ts.map +1 -0
  155. package/dist/cli.js +5 -0
  156. package/dist/config.d.ts +11 -0
  157. package/dist/config.d.ts.map +1 -0
  158. package/dist/config.js +9 -0
  159. package/dist/index.d.ts +8 -0
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +7 -0
  162. package/package.json +92 -0
@@ -0,0 +1,468 @@
1
+ import { bold } from "ansis";
2
+ import { toKebabCase } from "remeda";
3
+ import { commandNames } from "./command-names.js";
4
+ import { accent, dimmed } from "./lib/io.js";
5
+ const COMMAND_GROUPS = [
6
+ {
7
+ heading: "ACCOUNT",
8
+ commands: [commandNames.login, commandNames.logout, commandNames.me],
9
+ },
10
+ {
11
+ heading: "RESOURCES",
12
+ commands: [
13
+ commandNames.org.root,
14
+ commandNames.project.root,
15
+ commandNames.invite,
16
+ commandNames.upgrade,
17
+ ],
18
+ },
19
+ {
20
+ heading: "AUTOMATIONS",
21
+ commands: [commandNames.deploy],
22
+ },
23
+ {
24
+ heading: "OTHER",
25
+ commands: [commandNames.accept, commandNames.debug],
26
+ },
27
+ ];
28
+ /**
29
+ * Prints CLI help with Automate.ax's own accent color and grouped root commands.
30
+ *
31
+ * @param cmd - Command definition to render.
32
+ * @param parent - Parent definition for a nested command.
33
+ */
34
+ export const showUsage = async (cmd, parent) => {
35
+ // This is also used by Citty for routing errors. Keep usage on stdout for
36
+ // now, matching Citty's callback shape, while the error line goes to stderr.
37
+ console.log((await renderUsage(cmd, parent)) + "\n");
38
+ };
39
+ /**
40
+ * Renders command usage text without Citty's built-in cyan styling.
41
+ *
42
+ * @param cmd - Command definition to render.
43
+ * @param parent - Parent definition for a nested command.
44
+ */
45
+ async function renderUsage(cmd, parent) {
46
+ if (!parent && (await resolveMeta(cmd)).name === commandNames.root) {
47
+ return renderRootUsage(cmd);
48
+ }
49
+ return renderCommandUsage(cmd, parent);
50
+ }
51
+ /**
52
+ * Renders the top-level help screen with commands grouped by user intent.
53
+ *
54
+ * @param cmd - Root command definition to render.
55
+ */
56
+ async function renderRootUsage(cmd) {
57
+ const meta = await resolveMeta(cmd);
58
+ const commandName = meta.name ?? commandNames.root;
59
+ const optionRows = createOptionRows(await resolveArgs(cmd));
60
+ const subCommands = await resolveSubCommands(cmd);
61
+ const lines = [
62
+ dimmed `${meta.description ?? "Automate.ax CLI"} (${commandName}${meta.version ? ` v${meta.version}` : ""})`,
63
+ "",
64
+ `${bold.underline `USAGE`} ${renderCommandUsageLine(commandName, `${optionRows.length > 0 ? "[OPTIONS] " : ""}<command>`)}`,
65
+ "",
66
+ ];
67
+ if (optionRows.length > 0) {
68
+ lines.push(bold.underline `OPTIONS`, "", formatRows(optionRows), "");
69
+ }
70
+ lines.push(bold.underline `COMMANDS`, "");
71
+ const groupedCommands = new Set(COMMAND_GROUPS.flatMap((group) => group.commands));
72
+ const groupRows = await Promise.all(COMMAND_GROUPS.map(async (group) => ({
73
+ ...group,
74
+ rows: await commandRows(group.commands, subCommands),
75
+ })));
76
+ const otherRows = await commandRows(Object.keys(subCommands).filter((name) => !groupedCommands.has(name)), subCommands);
77
+ const rootWidth = getMaxLabelWidth([
78
+ ...groupRows.flatMap(({ rows }) => rows),
79
+ ...otherRows,
80
+ ]);
81
+ for (const group of COMMAND_GROUPS) {
82
+ const rows = groupRows.find(({ heading }) => heading === group.heading)?.rows ?? [];
83
+ if (rows.length === 0)
84
+ continue;
85
+ lines.push(dimmed(group.heading));
86
+ lines.push(formatRows(rows, rootWidth));
87
+ lines.push("");
88
+ }
89
+ if (otherRows.length > 0) {
90
+ lines.push(dimmed `OTHER`);
91
+ lines.push(formatRows(otherRows, rootWidth));
92
+ lines.push("");
93
+ }
94
+ lines.push(`${accent `${commandName} <command> --help`} for more information about a command.`);
95
+ return lines.join("\n");
96
+ }
97
+ /**
98
+ * Renders non-root command help while preserving Citty's layout conventions.
99
+ *
100
+ * @param cmd - Command definition to render.
101
+ * @param parent - Parent definition used to qualify the command name.
102
+ */
103
+ async function renderCommandUsage(cmd, parent) {
104
+ const meta = await resolveMeta(cmd);
105
+ const parentMeta = parent ? await resolveMeta(parent) : {};
106
+ const commandName = renderCommandName(meta, parentMeta);
107
+ const optionRows = [];
108
+ const positionalRows = [];
109
+ const usageParts = [];
110
+ for (const arg of await resolveArgs(cmd)) {
111
+ if (arg.type === "positional") {
112
+ const name = renderPositionalName(arg);
113
+ const required = arg.required !== false && arg.default === undefined;
114
+ positionalRows.push(createRow(renderPositionalLabel(name), renderDescription(arg, required)));
115
+ usageParts.push(required ? `<${name}>` : `[${name}]`);
116
+ continue;
117
+ }
118
+ optionRows.push(...createOptionRows([arg]));
119
+ if (arg.required === true && arg.default === undefined) {
120
+ usageParts.push(`--${toKebabCase(arg.name)}${renderValueHint(arg)}`);
121
+ }
122
+ }
123
+ const commandRows = await commandRowsForSubCommands(await resolveSubCommands(cmd));
124
+ if (commandRows.length > 0) {
125
+ usageParts.push("<command>");
126
+ }
127
+ const version = meta.version ?? parentMeta.version;
128
+ const lines = [
129
+ dimmed `${meta.description ?? ""} (${commandName}${version ? ` v${version}` : ""})`,
130
+ "",
131
+ `${bold.underline `USAGE`} ${renderCommandUsageLine(commandName, [
132
+ optionRows.length > 0 || positionalRows.length > 0 ? "[OPTIONS]" : "",
133
+ usageParts.join(" "),
134
+ ]
135
+ .filter(Boolean)
136
+ .join(" "))}`,
137
+ "",
138
+ ];
139
+ if (positionalRows.length > 0) {
140
+ lines.push(bold.underline `ARGUMENTS`, "", formatRows(positionalRows), "");
141
+ }
142
+ if (optionRows.length > 0) {
143
+ lines.push(bold.underline `OPTIONS`, "", formatRows(optionRows), "");
144
+ }
145
+ if (commandRows.length > 0) {
146
+ lines.push(bold.underline `COMMANDS`, "", formatRows(commandRows), "");
147
+ lines.push(`${accent `${commandName} <command> --help`} for more information about a command.`);
148
+ }
149
+ return lines.join("\n");
150
+ }
151
+ /**
152
+ * Builds display rows for non-positional options and their negated forms.
153
+ *
154
+ * @param args - Arguments whose options should be displayed.
155
+ */
156
+ function createOptionRows(args) {
157
+ const optionRows = [];
158
+ for (const arg of args) {
159
+ if (arg.type === "positional")
160
+ continue;
161
+ optionRows.push(createRow(renderOptionLabel(arg), renderOptionDescription(arg, arg.required === true && arg.default === undefined)));
162
+ if (shouldRenderNegativeFlag(arg)) {
163
+ optionRows.push(createRow(renderFlagLabel(`--no-${toKebabCase(arg.name)}`), renderNegativeDescription(arg)));
164
+ }
165
+ }
166
+ return optionRows;
167
+ }
168
+ /**
169
+ * Resolves command metadata, including lazy Citty values.
170
+ *
171
+ * @param cmd - Command-like value containing optional metadata.
172
+ * @param cmd.meta - Static or lazy metadata to resolve.
173
+ */
174
+ async function resolveMeta(cmd) {
175
+ return cmd.meta ? await resolveValue(cmd.meta) : {};
176
+ }
177
+ /**
178
+ * Resolves command args and normalizes aliases to arrays.
179
+ *
180
+ * @param cmd - Command whose argument definitions should be resolved.
181
+ */
182
+ async function resolveArgs(cmd) {
183
+ if (!cmd.args)
184
+ return [];
185
+ return Object.entries(await resolveValue(cmd.args)).map(([name, arg]) => ({
186
+ ...arg,
187
+ name,
188
+ alias: "alias" in arg
189
+ ? typeof arg.alias === "string"
190
+ ? [arg.alias]
191
+ : (arg.alias ?? [])
192
+ : [],
193
+ }));
194
+ }
195
+ /**
196
+ * Resolves visible subcommands by name.
197
+ *
198
+ * @param cmd - Command whose child definitions should be resolved.
199
+ */
200
+ async function resolveSubCommands(cmd) {
201
+ return Object.fromEntries((await Promise.all(Object.entries(cmd.subCommands ? await resolveValue(cmd.subCommands) : {}).map(async ([name, subCommand]) => {
202
+ const command = await resolveValue(subCommand);
203
+ return (await resolveMeta(command)).hidden
204
+ ? null
205
+ : [name, command];
206
+ }))).filter((entry) => entry != null));
207
+ }
208
+ /**
209
+ * Resolves Citty values that may be lazy or async.
210
+ *
211
+ * @param value - Static, promised, or lazily computed value.
212
+ */
213
+ async function resolveValue(value) {
214
+ if (isAwaitable(value)) {
215
+ return await value();
216
+ }
217
+ return await value;
218
+ }
219
+ /**
220
+ * Narrows Citty resolvables that use lazy functions.
221
+ *
222
+ * @param value - Resolvable value to inspect.
223
+ */
224
+ function isAwaitable(value) {
225
+ return typeof value === "function";
226
+ }
227
+ /**
228
+ * Builds display rows for requested subcommand names.
229
+ *
230
+ * @param names - Subcommand names to include, in display order.
231
+ * @param subCommands - Resolved subcommands indexed by name.
232
+ */
233
+ async function commandRows(names, subCommands) {
234
+ return (await Promise.all(names.map(async (name) => {
235
+ const command = subCommands[name];
236
+ if (!command)
237
+ return null;
238
+ const meta = await resolveMeta(command);
239
+ if (meta.hidden)
240
+ return null;
241
+ return createRow(name, meta.description ?? "");
242
+ }))).filter((row) => row != null);
243
+ }
244
+ /**
245
+ * Builds display rows for all visible child commands.
246
+ *
247
+ * @param subCommands - Resolved subcommands indexed by name.
248
+ */
249
+ async function commandRowsForSubCommands(subCommands) {
250
+ return commandRows(Object.keys(subCommands), subCommands);
251
+ }
252
+ /**
253
+ * Formats two-column CLI rows with right-aligned command labels.
254
+ *
255
+ * @param rows - Styled label and description rows to format.
256
+ * @param width - Display width reserved for the label column.
257
+ */
258
+ function formatRows(rows, width = getMaxLabelWidth(rows)) {
259
+ return rows
260
+ .map(({ description, label, width: labelWidth }) => {
261
+ const descriptionIndent = " ".repeat(2 + width + 2);
262
+ const [firstDescription = "", ...rest] = description.split("\n");
263
+ return [
264
+ ` ${" ".repeat(width - labelWidth)}${label} ${firstDescription}`,
265
+ ...rest.map((line) => `${descriptionIndent}${line}`),
266
+ ].join("\n");
267
+ })
268
+ .join("\n");
269
+ }
270
+ /**
271
+ * Creates an accent-styled row while preserving plain label width.
272
+ *
273
+ * @param label - Plain label to accent or an already styled syntax label.
274
+ * @param description - Description displayed beside the label.
275
+ */
276
+ function createRow(label, description) {
277
+ if (typeof label !== "string") {
278
+ return {
279
+ description,
280
+ label: label.text,
281
+ width: label.width,
282
+ };
283
+ }
284
+ return {
285
+ description,
286
+ label: accent(label),
287
+ width: label.length,
288
+ };
289
+ }
290
+ /**
291
+ * Finds max display width for row labels.
292
+ *
293
+ * @param rows - Rows whose label widths should be compared.
294
+ */
295
+ function getMaxLabelWidth(rows) {
296
+ return Math.max(...rows.map((row) => row.width));
297
+ }
298
+ /**
299
+ * Renders a usage line as command plus dimmed syntax placeholders.
300
+ *
301
+ * @param command - Command invocation to accent.
302
+ * @param syntax - Optional usage syntax appended in dimmed text.
303
+ */
304
+ function renderCommandUsageLine(command, syntax = "") {
305
+ return [accent(command), syntax ? dimmed ` ${syntax}` : ""].join("");
306
+ }
307
+ /**
308
+ * Renders help with the primary binary name, even for nested commands.
309
+ *
310
+ * @param meta - Metadata for the command being rendered.
311
+ * @param parentMeta - Metadata for its immediate parent command.
312
+ */
313
+ function renderCommandName(meta, parentMeta) {
314
+ const name = meta.name ?? commandNames.root;
315
+ if (!parentMeta.name)
316
+ return name;
317
+ if (parentMeta.name === commandNames.root)
318
+ return `${parentMeta.name} ${name}`;
319
+ return `${commandNames.root} ${parentMeta.name} ${name}`;
320
+ }
321
+ /**
322
+ * Renders a positional label as placeholder syntax.
323
+ *
324
+ * @param label - Plain positional placeholder.
325
+ */
326
+ function renderPositionalLabel(label) {
327
+ return {
328
+ text: dimmed(label),
329
+ width: label.length,
330
+ };
331
+ }
332
+ /**
333
+ * Renders positional arg names as readable CLI placeholders.
334
+ *
335
+ * @param arg - Positional argument definition.
336
+ */
337
+ function renderPositionalName(arg) {
338
+ return (arg.valueHint ?? toKebabCase(arg.name)).toUpperCase();
339
+ }
340
+ /**
341
+ * Renders option names with dimmed punctuation and placeholders.
342
+ *
343
+ * @param arg - Option definition, including aliases and value hints.
344
+ */
345
+ function renderOptionLabel(arg) {
346
+ const labels = [
347
+ ...arg.alias.map((alias) => `-${alias}`),
348
+ `--${toKebabCase(arg.name)}`,
349
+ ];
350
+ const lastIndex = labels.length - 1;
351
+ return joinSyntaxLabels(labels.map((label, index) => index === lastIndex
352
+ ? concatSyntaxLabels(renderFlagLabel(label), renderValueHintLabel(arg))
353
+ : renderFlagLabel(label)), { text: dimmed `, `, width: 2 });
354
+ }
355
+ /**
356
+ * Renders a flag name in accent color.
357
+ *
358
+ * @param label - Plain flag name.
359
+ */
360
+ function renderFlagLabel(label) {
361
+ return {
362
+ text: accent(label),
363
+ width: label.length,
364
+ };
365
+ }
366
+ /**
367
+ * Renders option value hints as dimmed syntax.
368
+ *
369
+ * @param arg - Option definition used to derive the placeholder.
370
+ */
371
+ function renderValueHintLabel(arg) {
372
+ const hint = renderValueHint(arg);
373
+ return {
374
+ text: dimmed(hint),
375
+ width: hint.length,
376
+ };
377
+ }
378
+ /**
379
+ * Joins syntax labels with styled separator text.
380
+ *
381
+ * @param labels - Styled syntax fragments to join.
382
+ * @param separator - Styled separator placed between fragments.
383
+ */
384
+ function joinSyntaxLabels(labels, separator) {
385
+ return {
386
+ text: labels.map((label) => label.text).join(separator.text),
387
+ width: labels.reduce((width, label) => width + label.width, 0) +
388
+ separator.width * Math.max(0, labels.length - 1),
389
+ };
390
+ }
391
+ /**
392
+ * Concatenates syntax labels without separators.
393
+ *
394
+ * @param labels - Styled syntax fragments to concatenate.
395
+ */
396
+ function concatSyntaxLabels(...labels) {
397
+ return {
398
+ text: labels.map((label) => label.text).join(""),
399
+ width: labels.reduce((width, label) => width + label.width, 0),
400
+ };
401
+ }
402
+ /**
403
+ * Renders value placeholders for options and positionals.
404
+ *
405
+ * @param arg - Argument definition used to derive the placeholder.
406
+ */
407
+ function renderValueHint(arg) {
408
+ if (arg.valueHint) {
409
+ return `=<${arg.valueHint}>`;
410
+ }
411
+ if (!arg.type || arg.type === "positional" || arg.type === "boolean") {
412
+ return "";
413
+ }
414
+ if (arg.type === "enum" && arg.options?.length) {
415
+ return `=<${arg.options.join("|")}>`;
416
+ }
417
+ return `=<${toKebabCase(arg.name)}>`;
418
+ }
419
+ /**
420
+ * Renders descriptions plus required/default hints.
421
+ *
422
+ * @param arg - Argument whose description and default should be rendered.
423
+ * @param required - Whether to append a required marker.
424
+ */
425
+ function renderDescription(arg, required) {
426
+ return [
427
+ arg.description,
428
+ required ? dimmed `(Required)` : "",
429
+ arg.default === undefined ? "" : dimmed `(Default: ${arg.default})`,
430
+ ]
431
+ .filter(Boolean)
432
+ .join(" ");
433
+ }
434
+ /**
435
+ * Renders option descriptions with defaults on their own indented line.
436
+ *
437
+ * @param arg - Option whose description and default should be rendered.
438
+ * @param required - Whether to append a required marker.
439
+ */
440
+ function renderOptionDescription(arg, required) {
441
+ return [
442
+ [arg.description, required ? dimmed `(Required)` : ""]
443
+ .filter(Boolean)
444
+ .join(" "),
445
+ arg.default === undefined ? "" : dimmed `(Default: ${arg.default})`,
446
+ ]
447
+ .filter(Boolean)
448
+ .join("\n");
449
+ }
450
+ /**
451
+ * Renders descriptions for boolean negation flags.
452
+ *
453
+ * @param arg - Boolean option with an optional custom negation description.
454
+ */
455
+ function renderNegativeDescription(arg) {
456
+ if ("negativeDescription" in arg && arg.negativeDescription) {
457
+ return arg.negativeDescription;
458
+ }
459
+ return `Negate ${accent `--${toKebabCase(arg.name)}`}`;
460
+ }
461
+ /**
462
+ * Shows real Citty-supported negation for every boolean flag.
463
+ *
464
+ * @param arg - Argument to test for a generated negated form.
465
+ */
466
+ function shouldRenderNegativeFlag(arg) {
467
+ return arg.type === "boolean" && !/^no[-A-Z]/.test(arg.name);
468
+ }