effect 4.0.0-beta.12 → 4.0.0-beta.14

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 (86) hide show
  1. package/dist/ManagedRuntime.d.ts +1 -1
  2. package/dist/ManagedRuntime.js +1 -1
  3. package/dist/Schedule.js +1 -1
  4. package/dist/Schedule.js.map +1 -1
  5. package/dist/SchemaAST.js +1 -1
  6. package/dist/SchemaAST.js.map +1 -1
  7. package/dist/index.d.ts +47 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +47 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal/request.js +2 -2
  12. package/dist/internal/request.js.map +1 -1
  13. package/dist/internal/schema/annotations.js +2 -0
  14. package/dist/internal/schema/annotations.js.map +1 -1
  15. package/dist/unstable/ai/McpSchema.d.ts +36 -36
  16. package/dist/unstable/ai/McpSchema.js +8 -8
  17. package/dist/unstable/ai/McpSchema.js.map +1 -1
  18. package/dist/unstable/cli/CliOutput.js +24 -2
  19. package/dist/unstable/cli/CliOutput.js.map +1 -1
  20. package/dist/unstable/cli/Command.d.ts +41 -6
  21. package/dist/unstable/cli/Command.d.ts.map +1 -1
  22. package/dist/unstable/cli/Command.js +69 -30
  23. package/dist/unstable/cli/Command.js.map +1 -1
  24. package/dist/unstable/cli/GlobalFlag.d.ts +160 -0
  25. package/dist/unstable/cli/GlobalFlag.d.ts.map +1 -0
  26. package/dist/unstable/cli/GlobalFlag.js +157 -0
  27. package/dist/unstable/cli/GlobalFlag.js.map +1 -0
  28. package/dist/unstable/cli/HelpDoc.d.ts +10 -0
  29. package/dist/unstable/cli/HelpDoc.d.ts.map +1 -1
  30. package/dist/unstable/cli/index.d.ts +4 -0
  31. package/dist/unstable/cli/index.d.ts.map +1 -1
  32. package/dist/unstable/cli/index.js +4 -0
  33. package/dist/unstable/cli/index.js.map +1 -1
  34. package/dist/unstable/cli/internal/command.d.ts +1 -5
  35. package/dist/unstable/cli/internal/command.d.ts.map +1 -1
  36. package/dist/unstable/cli/internal/command.js +4 -22
  37. package/dist/unstable/cli/internal/command.js.map +1 -1
  38. package/dist/unstable/cli/internal/help.d.ts +19 -0
  39. package/dist/unstable/cli/internal/help.d.ts.map +1 -0
  40. package/dist/unstable/cli/internal/help.js +53 -0
  41. package/dist/unstable/cli/internal/help.js.map +1 -0
  42. package/dist/unstable/cli/internal/parser.js +20 -35
  43. package/dist/unstable/cli/internal/parser.js.map +1 -1
  44. package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
  45. package/dist/unstable/httpapi/OpenApi.js +3 -4
  46. package/dist/unstable/httpapi/OpenApi.js.map +1 -1
  47. package/dist/unstable/reactivity/Atom.d.ts +4 -4
  48. package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
  49. package/dist/unstable/rpc/Rpc.d.ts +1 -1
  50. package/dist/unstable/rpc/Rpc.d.ts.map +1 -1
  51. package/dist/unstable/schema/Model.d.ts +1 -1
  52. package/dist/unstable/schema/Model.d.ts.map +1 -1
  53. package/dist/unstable/schema/VariantSchema.d.ts +3 -3
  54. package/dist/unstable/schema/VariantSchema.d.ts.map +1 -1
  55. package/dist/unstable/schema/VariantSchema.js +3 -3
  56. package/dist/unstable/schema/VariantSchema.js.map +1 -1
  57. package/dist/unstable/sql/SqlError.d.ts +14 -14
  58. package/dist/unstable/sql/SqlError.d.ts.map +1 -1
  59. package/dist/unstable/sql/SqlError.js +9 -3
  60. package/dist/unstable/sql/SqlError.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/ManagedRuntime.ts +1 -1
  63. package/src/Schedule.ts +1 -1
  64. package/src/SchemaAST.ts +1 -1
  65. package/src/index.ts +47 -0
  66. package/src/internal/request.ts +2 -2
  67. package/src/internal/schema/annotations.ts +2 -0
  68. package/src/unstable/ai/McpSchema.ts +8 -8
  69. package/src/unstable/cli/CliOutput.ts +32 -2
  70. package/src/unstable/cli/Command.ts +104 -37
  71. package/src/unstable/cli/GlobalFlag.ts +321 -0
  72. package/src/unstable/cli/HelpDoc.ts +12 -0
  73. package/src/unstable/cli/index.ts +5 -0
  74. package/src/unstable/cli/internal/command.ts +5 -30
  75. package/src/unstable/cli/internal/help.ts +65 -0
  76. package/src/unstable/cli/internal/parser.ts +23 -52
  77. package/src/unstable/httpapi/OpenApi.ts +3 -4
  78. package/src/unstable/reactivity/Atom.ts +6 -6
  79. package/src/unstable/rpc/Rpc.ts +1 -1
  80. package/src/unstable/schema/VariantSchema.ts +6 -6
  81. package/src/unstable/sql/SqlError.ts +11 -9
  82. package/dist/unstable/cli/internal/builtInFlags.d.ts +0 -7
  83. package/dist/unstable/cli/internal/builtInFlags.d.ts.map +0 -1
  84. package/dist/unstable/cli/internal/builtInFlags.js +0 -44
  85. package/dist/unstable/cli/internal/builtInFlags.js.map +0 -1
  86. package/src/unstable/cli/internal/builtInFlags.ts +0 -78
@@ -6,7 +6,8 @@ import * as Console from "../../Console.ts"
6
6
  import * as Effect from "../../Effect.ts"
7
7
  import type * as FileSystem from "../../FileSystem.ts"
8
8
  import { dual } from "../../Function.ts"
9
- import type * as Layer from "../../Layer.ts"
9
+ import * as Layer from "../../Layer.ts"
10
+ import * as Option from "../../Option.ts"
10
11
  import type * as Path from "../../Path.ts"
11
12
  import type { Pipeable } from "../../Pipeable.ts"
12
13
  import * as Predicate from "../../Predicate.ts"
@@ -18,13 +19,13 @@ import type { NoInfer, Simplify } from "../../Types.ts"
18
19
  import type { ChildProcessSpawner } from "../process/ChildProcessSpawner.ts"
19
20
  import * as CliError from "./CliError.ts"
20
21
  import * as CliOutput from "./CliOutput.ts"
21
- import { checkForDuplicateFlags, getHelpForCommandPath, makeCommand, toImpl, TypeId } from "./internal/command.ts"
22
- import * as CommandDescriptor from "./internal/completions/CommandDescriptor.ts"
23
- import * as Completions from "./internal/completions/Completions.ts"
22
+ import * as GlobalFlag from "./GlobalFlag.ts"
23
+ import { checkForDuplicateFlags, makeCommand, toImpl, TypeId } from "./internal/command.ts"
24
24
  import { parseConfig } from "./internal/config.ts"
25
+ import { getHelpForCommandPath } from "./internal/help.ts"
25
26
  import * as Lexer from "./internal/lexer.ts"
26
27
  import * as Parser from "./internal/parser.ts"
27
- import type * as Param from "./Param.ts"
28
+ import * as Param from "./Param.ts"
28
29
 
29
30
  /* ========================================================================== */
30
31
  /* Public Types */
@@ -100,6 +101,11 @@ export interface Command<Name extends string, Input, E = never, R = never> exten
100
101
  */
101
102
  readonly shortDescription: string | undefined
102
103
 
104
+ /**
105
+ * An optional alias that can be used as a shorter command name.
106
+ */
107
+ readonly alias: string | undefined
108
+
103
109
  /**
104
110
  * Optional usage examples for the command.
105
111
  */
@@ -934,6 +940,7 @@ export const withSubcommands: {
934
940
  config: impl.config,
935
941
  description: impl.description,
936
942
  shortDescription: impl.shortDescription,
943
+ alias: impl.alias,
937
944
  annotations: impl.annotations,
938
945
  examples: impl.examples,
939
946
  service: impl.service,
@@ -1073,6 +1080,43 @@ export const withShortDescription: {
1073
1080
  shortDescription: string
1074
1081
  ) => makeCommand({ ...toImpl(self), shortDescription }))
1075
1082
 
1083
+ /**
1084
+ * Sets an alias for a command.
1085
+ *
1086
+ * Aliases are accepted as alternate subcommand names during parsing and are
1087
+ * shown in help output as `name, alias`.
1088
+ *
1089
+ * @since 4.0.0
1090
+ * @category combinators
1091
+ */
1092
+ export const withAlias: {
1093
+ /**
1094
+ * Sets an alias for a command.
1095
+ *
1096
+ * Aliases are accepted as alternate subcommand names during parsing and are
1097
+ * shown in help output as `name, alias`.
1098
+ *
1099
+ * @since 4.0.0
1100
+ * @category combinators
1101
+ */
1102
+ (alias: string): <const Name extends string, Input, E, R>(
1103
+ self: Command<Name, Input, E, R>
1104
+ ) => Command<Name, Input, E, R>
1105
+ /**
1106
+ * Sets an alias for a command.
1107
+ *
1108
+ * Aliases are accepted as alternate subcommand names during parsing and are
1109
+ * shown in help output as `name, alias`.
1110
+ *
1111
+ * @since 4.0.0
1112
+ * @category combinators
1113
+ */
1114
+ <const Name extends string, Input, E, R>(self: Command<Name, Input, E, R>, alias: string): Command<Name, Input, E, R>
1115
+ } = dual(2, <const Name extends string, Input, E, R>(
1116
+ self: Command<Name, Input, E, R>,
1117
+ alias: string
1118
+ ) => makeCommand({ ...toImpl(self), alias }))
1119
+
1076
1120
  /**
1077
1121
  * Adds a custom annotation to a command.
1078
1122
  *
@@ -1497,7 +1541,7 @@ const showHelp = <Name extends string, Input, E, R>(
1497
1541
  ): Effect.Effect<void, never, Environment> =>
1498
1542
  Effect.gen(function*() {
1499
1543
  const formatter = yield* CliOutput.Formatter
1500
- const helpDoc = getHelpForCommandPath(command, commandPath)
1544
+ const helpDoc = yield* getHelpForCommandPath(command, commandPath, GlobalFlag.Registry)
1501
1545
  yield* Console.log(formatter.formatHelpDoc(helpDoc))
1502
1546
  if (errors && errors.length > 0) {
1503
1547
  yield* Console.error(formatter.formatErrors(errors))
@@ -1553,12 +1597,13 @@ export const run: {
1553
1597
  * @since 4.0.0
1554
1598
  * @category command execution
1555
1599
  */
1556
- <Name extends string, Input, E, R>(
1557
- command: Command<Name, Input, E, R>,
1600
+ (
1558
1601
  config: {
1559
1602
  readonly version: string
1560
1603
  }
1561
- ): Effect.Effect<void, E | CliError.CliError, R | Environment>
1604
+ ): <Name extends string, Input, E, R>(
1605
+ command: Command<Name, Input, E, R>
1606
+ ) => Effect.Effect<void, E | CliError.CliError, R | Environment>
1562
1607
  /**
1563
1608
  * Runs a command with the provided input arguments.
1564
1609
  *
@@ -1583,13 +1628,12 @@ export const run: {
1583
1628
  * @since 4.0.0
1584
1629
  * @category command execution
1585
1630
  */
1586
- (
1631
+ <Name extends string, Input, E, R>(
1632
+ command: Command<Name, Input, E, R>,
1587
1633
  config: {
1588
1634
  readonly version: string
1589
1635
  }
1590
- ): <Name extends string, Input, E, R>(
1591
- command: Command<Name, Input, E, R>
1592
- ) => Effect.Effect<void, E | CliError.CliError, R | Environment>
1636
+ ): Effect.Effect<void, E | CliError.CliError, R | Environment>
1593
1637
  } = dual(2, <Name extends string, Input, E, R>(
1594
1638
  command: Command<Name, Input, E, R>,
1595
1639
  config: {
@@ -1652,29 +1696,37 @@ export const runWith = <const Name extends string, Input, E, R>(
1652
1696
  const commandImpl = toImpl(command)
1653
1697
  return Effect.fnUntraced(
1654
1698
  function*(args: ReadonlyArray<string>) {
1655
- // Lex and extract built-in flags
1656
1699
  const { tokens, trailingOperands } = Lexer.lex(args)
1657
- const { completions, help, logLevel, remainder, version } = yield* Parser.extractBuiltInOptions(tokens)
1700
+
1701
+ // 1. Read global flags from registry
1702
+ const flags = Array.from(yield* GlobalFlag.Registry)
1703
+
1704
+ // 2. Extract global flag tokens
1705
+ const allFlagParams = flags.flatMap((f) => Param.extractSingleParams(f.flag))
1706
+ const globalRegistry = Parser.createFlagRegistry(allFlagParams.filter(Param.isFlagParam))
1707
+ const { flagMap, remainder } = Parser.consumeKnownFlags(tokens, globalRegistry)
1708
+ const emptyArgs: Param.ParsedArgs = { flags: flagMap, arguments: [] }
1709
+
1710
+ // 3. Parse command arguments from remaining tokens
1658
1711
  const parsedArgs = yield* Parser.parseArgs({ tokens: remainder, trailingOperands }, command)
1659
1712
  const commandPath = [command.name, ...Parser.getCommandPath(parsedArgs)] as const
1660
- const formatter = yield* CliOutput.Formatter
1661
-
1662
- // Handle built-in flags (early exits)
1663
- if (help) {
1664
- yield* Console.log(formatter.formatHelpDoc(getHelpForCommandPath(command, commandPath)))
1665
- return
1666
- }
1667
- if (completions !== undefined) {
1668
- const descriptor = CommandDescriptor.fromCommand(command)
1669
- yield* Console.log(Completions.generate(command.name, completions, descriptor))
1670
- return
1671
- }
1672
- if (version) {
1673
- yield* Console.log(formatter.formatVersion(command.name, config.version))
1713
+ const handlerCtx: GlobalFlag.HandlerContext = { command, commandPath, version: config.version }
1714
+
1715
+ // 4. Process action flags first present action wins, then exit
1716
+ for (const flag of flags) {
1717
+ if (flag._tag !== "Action") continue
1718
+ const singles = Param.extractSingleParams(flag.flag)
1719
+ const hasEntry = singles.some((s) => {
1720
+ const entries = flagMap[s.name]
1721
+ return entries !== undefined && entries.length > 0
1722
+ })
1723
+ if (!hasEntry) continue
1724
+ const [, value] = yield* flag.flag.parse(emptyArgs)
1725
+ yield* flag.run(value, handlerCtx)
1674
1726
  return
1675
1727
  }
1676
1728
 
1677
- // Handle parsing errors
1729
+ // 5. Handle parsing errors
1678
1730
  if (parsedArgs.errors && parsedArgs.errors.length > 0) {
1679
1731
  return yield* showHelp(command, commandPath, parsedArgs.errors)
1680
1732
  }
@@ -1682,15 +1734,30 @@ export const runWith = <const Name extends string, Input, E, R>(
1682
1734
  if (parseResult._tag === "Failure") {
1683
1735
  return yield* showHelp(command, commandPath, [parseResult.failure])
1684
1736
  }
1685
- const parsed = parseResult.success
1686
1737
 
1687
- // Create and run the execution program
1688
- const program = commandImpl.handle(parsed, [command.name])
1689
- const withLogLevel = logLevel !== undefined
1690
- ? Effect.provideService(program, References.MinimumLogLevel, logLevel)
1691
- : program
1738
+ // 6. Provide setting values
1739
+ let contextLayer: Layer.Layer<never> = Layer.empty
1740
+ for (const flag of flags) {
1741
+ if (flag._tag !== "Setting") continue
1742
+ const [, value] = yield* flag.flag.parse(emptyArgs)
1743
+ contextLayer = Layer.merge(contextLayer, Layer.succeed(flag, value))
1744
+ }
1745
+
1746
+ // 7. Apply built-in setting behavior
1747
+ if (flags.includes(GlobalFlag.LogLevel)) {
1748
+ const [, logLevel] = yield* GlobalFlag.LogLevel.flag.parse(emptyArgs)
1749
+ contextLayer = Layer.merge(
1750
+ contextLayer,
1751
+ Option.match(logLevel, {
1752
+ onNone: () => Layer.empty,
1753
+ onSome: (level) => Layer.succeed(References.MinimumLogLevel, level)
1754
+ })
1755
+ )
1756
+ }
1692
1757
 
1693
- yield* withLogLevel
1758
+ // 8. Run command handler with composed context
1759
+ const program = commandImpl.handle(parseResult.success, [command.name])
1760
+ yield* Effect.provide(program, contextLayer)
1694
1761
  },
1695
1762
  Effect.catchIf(
1696
1763
  ((error: any) =>
@@ -0,0 +1,321 @@
1
+ /**
2
+ * @since 4.0.0
3
+ */
4
+
5
+ import * as Console from "../../Console.ts"
6
+ import * as Effect from "../../Effect.ts"
7
+ import { dual } from "../../Function.ts"
8
+ import type { LogLevel as LogLevelType } from "../../LogLevel.ts"
9
+ import * as Option from "../../Option.ts"
10
+ import * as ServiceMap from "../../ServiceMap.ts"
11
+ import * as CliOutput from "./CliOutput.ts"
12
+ import type * as Command from "./Command.ts"
13
+ import * as Flag from "./Flag.ts"
14
+
15
+ /* ========================================================================== */
16
+ /* Types */
17
+ /* ========================================================================== */
18
+
19
+ /**
20
+ * Context passed to action handlers.
21
+ *
22
+ * @since 4.0.0
23
+ * @category models
24
+ */
25
+ export interface HandlerContext {
26
+ readonly command: Command.Command<any, unknown, unknown, unknown>
27
+ readonly commandPath: ReadonlyArray<string>
28
+ readonly version: string
29
+ }
30
+
31
+ /**
32
+ * Action flag: side effect + exit (--help, --version, --completions).
33
+ *
34
+ * @since 4.0.0
35
+ * @category models
36
+ */
37
+ export interface Action<A> {
38
+ readonly _tag: "Action"
39
+ readonly flag: Flag.Flag<A>
40
+ readonly run: (
41
+ value: A,
42
+ context: HandlerContext
43
+ ) => Effect.Effect<void>
44
+ }
45
+
46
+ /**
47
+ * Setting flag: configure command handler's environment (--log-level, --config).
48
+ *
49
+ * @since 4.0.0
50
+ * @category models
51
+ */
52
+ export interface Setting<A> extends ServiceMap.Reference<A> {
53
+ readonly _tag: "Setting"
54
+ readonly flag: Flag.Flag<A>
55
+ }
56
+
57
+ /**
58
+ * Global flag discriminated union.
59
+ *
60
+ * @since 4.0.0
61
+ * @category models
62
+ */
63
+ export type GlobalFlag<A> = Action<A> | Setting<A>
64
+
65
+ /* ========================================================================== */
66
+ /* Constructors */
67
+ /* ========================================================================== */
68
+
69
+ /**
70
+ * Creates an Action flag that performs a side effect and exits.
71
+ *
72
+ * @since 4.0.0
73
+ * @category constructors
74
+ */
75
+ export const action = <A>(options: {
76
+ readonly flag: Flag.Flag<A>
77
+ readonly run: (
78
+ value: A,
79
+ context: HandlerContext
80
+ ) => Effect.Effect<void>
81
+ }): Action<A> => ({
82
+ _tag: "Action",
83
+ flag: options.flag,
84
+ run: options.run
85
+ })
86
+
87
+ /**
88
+ * Creates a Setting flag that configures the command handler's environment.
89
+ *
90
+ * @since 4.0.0
91
+ * @category constructors
92
+ */
93
+ export const setting = <A>(options: {
94
+ readonly flag: Flag.Flag<A>
95
+ readonly defaultValue: () => A
96
+ }): Setting<A> => {
97
+ settingIdCounter += 1
98
+ const ref = ServiceMap.Reference<A>(
99
+ `effect/cli/GlobalFlag/Setting/${settingIdCounter}`,
100
+ { defaultValue: options.defaultValue }
101
+ )
102
+ return Object.assign(ref, {
103
+ _tag: "Setting" as const,
104
+ flag: options.flag
105
+ })
106
+ }
107
+
108
+ let settingIdCounter = 0
109
+
110
+ /* ========================================================================== */
111
+ /* Built-in Flag References */
112
+ /* ========================================================================== */
113
+
114
+ import * as CommandDescriptor from "./internal/completions/CommandDescriptor.ts"
115
+ import * as CompletionsInternal from "./internal/completions/Completions.ts"
116
+ import * as HelpInternal from "./internal/help.ts"
117
+
118
+ /**
119
+ * The `--help` / `-h` global flag.
120
+ * Shows help documentation for the command.
121
+ *
122
+ * @since 4.0.0
123
+ * @category references
124
+ */
125
+ export const Help: Action<boolean> = action({
126
+ flag: Flag.boolean("help").pipe(
127
+ Flag.withAlias("h"),
128
+ Flag.withDescription("Show help information")
129
+ ),
130
+ run: (_, { command, commandPath }) =>
131
+ Effect.gen(function*() {
132
+ const formatter = yield* CliOutput.Formatter
133
+ const helpDoc = yield* HelpInternal.getHelpForCommandPath(command, commandPath, Registry)
134
+ yield* Console.log(formatter.formatHelpDoc(helpDoc))
135
+ })
136
+ })
137
+
138
+ /**
139
+ * The `--version` global flag.
140
+ * Shows version information for the command.
141
+ *
142
+ * @since 4.0.0
143
+ * @category references
144
+ */
145
+ export const Version: Action<boolean> = action({
146
+ flag: Flag.boolean("version").pipe(
147
+ Flag.withDescription("Show version information")
148
+ ),
149
+ run: (_, { command, version }) =>
150
+ Effect.gen(function*() {
151
+ const formatter = yield* CliOutput.Formatter
152
+ yield* Console.log(formatter.formatVersion(command.name, version))
153
+ })
154
+ })
155
+
156
+ /**
157
+ * The `--completions` global flag.
158
+ * Prints shell completion script for the given shell.
159
+ *
160
+ * @since 4.0.0
161
+ * @category references
162
+ */
163
+ export const Completions: Action<Option.Option<"bash" | "zsh" | "fish">> = action({
164
+ flag: Flag.choice("completions", ["bash", "zsh", "fish", "sh"] as const)
165
+ .pipe(
166
+ Flag.optional,
167
+ Flag.map((v) => Option.map(v, (s) => s === "sh" ? "bash" : s)),
168
+ Flag.withDescription("Print shell completion script")
169
+ ),
170
+ run: (shell, { command }) =>
171
+ Effect.gen(function*() {
172
+ if (Option.isNone(shell)) return
173
+ const descriptor = CommandDescriptor.fromCommand(command)
174
+ yield* Console.log(
175
+ CompletionsInternal.generate(command.name, shell.value, descriptor)
176
+ )
177
+ })
178
+ })
179
+
180
+ /**
181
+ * The `--log-level` global flag.
182
+ * Sets the minimum log level for the command.
183
+ *
184
+ * @since 4.0.0
185
+ * @category references
186
+ */
187
+ export const LogLevel: Setting<Option.Option<LogLevelType>> = setting({
188
+ flag: Flag.choiceWithValue(
189
+ "log-level",
190
+ [
191
+ ["all", "All"],
192
+ ["trace", "Trace"],
193
+ ["debug", "Debug"],
194
+ ["info", "Info"],
195
+ ["warn", "Warn"],
196
+ ["warning", "Warn"],
197
+ ["error", "Error"],
198
+ ["fatal", "Fatal"],
199
+ ["none", "None"]
200
+ ] as const
201
+ ).pipe(
202
+ Flag.optional,
203
+ Flag.withDescription("Sets the minimum log level")
204
+ ),
205
+ defaultValue: () => Option.none()
206
+ })
207
+
208
+ /* ========================================================================== */
209
+ /* Registry */
210
+ /* ========================================================================== */
211
+
212
+ /**
213
+ * The ordered set of global flag references.
214
+ * The parser iterates this set to know which flags to extract.
215
+ *
216
+ * @since 4.0.0
217
+ * @category references
218
+ */
219
+ export const Registry: ServiceMap.Reference<
220
+ Set<GlobalFlag<any>>
221
+ > = ServiceMap.Reference("effect/cli/GlobalFlag/Registry", {
222
+ defaultValue: () =>
223
+ new Set<GlobalFlag<any>>([
224
+ Help,
225
+ Version,
226
+ Completions,
227
+ LogLevel
228
+ ])
229
+ })
230
+
231
+ /* ========================================================================== */
232
+ /* Public API */
233
+ /* ========================================================================== */
234
+
235
+ /**
236
+ * Adds a global flag to the registry.
237
+ *
238
+ * @since 4.0.0
239
+ * @category modifiers
240
+ */
241
+ export const add: {
242
+ /* ========================================================================== */
243
+ /* Public API */
244
+ /* ========================================================================== */
245
+
246
+ /**
247
+ * Adds a global flag to the registry.
248
+ *
249
+ * @since 4.0.0
250
+ * @category modifiers
251
+ */
252
+ <A>(flag: GlobalFlag<A>): <B, E, R>(
253
+ self: Effect.Effect<B, E, R>
254
+ ) => Effect.Effect<B, E, R>
255
+ /* ========================================================================== */
256
+ /* Public API */
257
+ /* ========================================================================== */
258
+
259
+ /**
260
+ * Adds a global flag to the registry.
261
+ *
262
+ * @since 4.0.0
263
+ * @category modifiers
264
+ */
265
+ <B, E, R, A>(self: Effect.Effect<B, E, R>, flag: GlobalFlag<A>): Effect.Effect<B, E, R>
266
+ } = dual(
267
+ 2,
268
+ Effect.fnUntraced(function*<B, E, R, A>(
269
+ self: Effect.Effect<B, E, R>,
270
+ flag: GlobalFlag<A>
271
+ ) {
272
+ const currentRegistry = yield* Registry
273
+ const nextRegistry = new Set([...currentRegistry, flag])
274
+ return yield* Effect.provideService(self, Registry, nextRegistry)
275
+ })
276
+ )
277
+
278
+ /**
279
+ * Removes a global flag by its reference.
280
+ *
281
+ * @since 4.0.0
282
+ * @category modifiers
283
+ */
284
+ export const remove: {
285
+ /**
286
+ * Removes a global flag by its reference.
287
+ *
288
+ * @since 4.0.0
289
+ * @category modifiers
290
+ */
291
+ <A>(flag: GlobalFlag<A>): <B, E, R>(
292
+ self: Effect.Effect<B, E, R>
293
+ ) => Effect.Effect<B, E, R>
294
+ /**
295
+ * Removes a global flag by its reference.
296
+ *
297
+ * @since 4.0.0
298
+ * @category modifiers
299
+ */
300
+ <B, E, R, A>(self: Effect.Effect<B, E, R>, flag: GlobalFlag<A>): Effect.Effect<B, E, R>
301
+ } = dual(
302
+ 2,
303
+ Effect.fnUntraced(function*<B, E, R, A>(
304
+ self: Effect.Effect<B, E, R>,
305
+ flag: GlobalFlag<A>
306
+ ) {
307
+ const currentRegistry = yield* Registry
308
+ const nextRegistry = new Set(currentRegistry)
309
+ nextRegistry.delete(flag)
310
+ return yield* Effect.provideService(self, Registry, nextRegistry)
311
+ })
312
+ )
313
+
314
+ /**
315
+ * Removes all global flags (built-in and user-defined).
316
+ *
317
+ * @since 4.0.0
318
+ * @category modifiers
319
+ */
320
+ export const clear = <B, E, R>(self: Effect.Effect<B, E, R>): Effect.Effect<B, E, R> =>
321
+ Effect.provideService(self, Registry, new Set())
@@ -67,6 +67,11 @@ export interface HelpDoc {
67
67
  */
68
68
  readonly flags: ReadonlyArray<FlagDoc>
69
69
 
70
+ /**
71
+ * Global flags available to all commands (e.g., --help, --version).
72
+ */
73
+ readonly globalFlags?: ReadonlyArray<FlagDoc>
74
+
70
75
  /**
71
76
  * Custom command annotations.
72
77
  */
@@ -170,12 +175,14 @@ export interface FlagDoc {
170
175
  *
171
176
  * const deploySubcommand: HelpDoc.SubcommandDoc = {
172
177
  * name: "deploy",
178
+ * alias: "d",
173
179
  * shortDescription: "Deploy app",
174
180
  * description: "Deploy the application to the cloud"
175
181
  * }
176
182
  *
177
183
  * const buildSubcommand: HelpDoc.SubcommandDoc = {
178
184
  * name: "build",
185
+ * alias: undefined,
179
186
  * shortDescription: undefined,
180
187
  * description: "Build the application for production"
181
188
  * }
@@ -202,6 +209,11 @@ export interface SubcommandDoc {
202
209
  */
203
210
  readonly name: string
204
211
 
212
+ /**
213
+ * Optional short alias for invoking the subcommand.
214
+ */
215
+ readonly alias: string | undefined
216
+
205
217
  /**
206
218
  * Optional short description of what the subcommand does.
207
219
  */
@@ -29,6 +29,11 @@ export * as Command from "./Command.ts"
29
29
  */
30
30
  export * as Flag from "./Flag.ts"
31
31
 
32
+ /**
33
+ * @since 4.0.0
34
+ */
35
+ export * as GlobalFlag from "./GlobalFlag.ts"
36
+
32
37
  /**
33
38
  * @since 4.0.0
34
39
  */
@@ -90,6 +90,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
90
90
  readonly annotations?: ServiceMap.ServiceMap<never> | undefined
91
91
  readonly description?: string | undefined
92
92
  readonly shortDescription?: string | undefined
93
+ readonly alias?: string | undefined
93
94
  readonly examples?: ReadonlyArray<Command.Example> | undefined
94
95
  readonly subcommands?: ReadonlyArray<SubcommandGroup> | undefined
95
96
  readonly parse?: ((input: ParsedTokens) => Effect.Effect<Input, CliError.CliError, Environment>) | undefined
@@ -165,6 +166,7 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
165
166
  group: group.group,
166
167
  commands: Arr.map(group.commands, (subcommand) => ({
167
168
  name: subcommand.name,
169
+ alias: subcommand.alias,
168
170
  shortDescription: subcommand.shortDescription,
169
171
  description: subcommand.description ?? ""
170
172
  }))
@@ -200,6 +202,9 @@ export const makeCommand = <const Name extends string, Input, E, R>(options: {
200
202
  : {}),
201
203
  ...(Predicate.isNotUndefined(options.shortDescription)
202
204
  ? { shortDescription: options.shortDescription }
205
+ : {}),
206
+ ...(Predicate.isNotUndefined(options.alias)
207
+ ? { alias: options.alias }
203
208
  : {})
204
209
  })
205
210
  }
@@ -269,33 +274,3 @@ export const checkForDuplicateFlags = <Name extends string, Input>(
269
274
  }
270
275
  }
271
276
  }
272
-
273
- /**
274
- * Helper function to get help documentation for a specific command path.
275
- * Navigates through the command hierarchy to find the right command.
276
- */
277
- export const getHelpForCommandPath = <Name extends string, Input, E, R>(
278
- command: Command<Name, Input, E, R>,
279
- commandPath: ReadonlyArray<string>
280
- ): HelpDoc => {
281
- let currentCommand: Command.Any = command
282
-
283
- // Navigate through the command path to find the target command
284
- for (let i = 1; i < commandPath.length; i++) {
285
- const subcommandName = commandPath[i]
286
- let subcommand: Command.Any | undefined = undefined
287
-
288
- for (const group of currentCommand.subcommands) {
289
- subcommand = group.commands.find((sub) => sub.name === subcommandName)
290
- if (subcommand) {
291
- break
292
- }
293
- }
294
-
295
- if (subcommand) {
296
- currentCommand = subcommand
297
- }
298
- }
299
-
300
- return toImpl(currentCommand).buildHelpDoc(commandPath)
301
- }