effect icon indicating copy to clipboard operation
effect copied to clipboard

feat(cli): add Command.withConditionalBehavior for predicate based wrapping of commands

Open ryanbas21 opened this issue 1 month ago • 3 comments

Type

  • [ ] Refactor
  • [x] Feature
  • [ ] Bug Fix
  • [ ] Optimization
  • [ ] Documentation Update

Description

This PR introduces a composable combinator approach for adding conditional behaviors to commands based on CLI arguments.

Rather than adding separate runWith* methods for each conditional behavior (which would lead to API explosion with runWithWizard, runWithHelp, runWithValidation, etc.), this PR provides a single, extensible combinator pattern that follows functional composition principles.

 Command.withConditionalBehavior(predicate, behavior)

A combinator that wraps a command to conditionally trigger a behavior based on the provided predicate function:

  const command = Command.make("greet", {
    name: Options.text("name")
  }, ({ name }) => Effect.log(`Hello, ${name}!`))
    .pipe(
      Command.withConditionalBehavior(
        (args) => args.length <= 1,  // Trigger when no args provided
        "wizard"                      // Run wizard mode
      )
    )

  const cli = Command.run(command, {
    name: "MyApp",
    version: "1.0.0"
  })

Parameters:

  • predicate: (args: ReadonlyArray) => boolean - Function that determines when to trigger the behavior
  • behavior: "wizard" | CustomBehaviorFunction - Either the built-in "wizard" mode or a custom function

Wizard mode on bare command:

Command.make("deploy", config).pipe(
  Command.withConditionalBehavior(
    (args) => args.length <= 1,
    "wizard"
  )
)
  Command.make("migrate", config).pipe(
    Command.withConditionalBehavior(
      (args) => args.includes("--interactive"),
      (cmd, args) => Effect.gen(function* () {
        // Custom prompting logic
        const confirmed = yield* promptForConfirmation()
        return confirmed ? args : Effect.fail(new UserCancelled())
      })
    )
  )

Related

  • Related Issue #5699
  • Closes #5699

ryanbas21 avatar Nov 07 '25 03:11 ryanbas21

🦋 Changeset detected

Latest commit: 7e629458108e53088b3621befd61df18673ae710

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@effect/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

changeset-bot[bot] avatar Nov 07 '25 03:11 changeset-bot[bot]

@ryanbas21 - thanks for the PR!

I'm not sure about having a separate run method with all the specifics around wizard mode. Because then you could make the argument that we should expose similar utilities for showing help documentation based on certain conditions, prompting on certain conditions, etc.

I think it would be nicer if a combinator could be exposed that would allow you to wrap a command and show the wizard mode based on a predicate.

IMax153 avatar Nov 07 '25 14:11 IMax153

@ryanbas21 - thanks for the PR!

I'm not sure about having a separate run method with all the specifics around wizard mode. Because then you could make the argument that we should expose similar utilities for showing help documentation based on certain conditions, prompting on certain conditions, etc.

I think it would be nicer if a combinator could be exposed that would allow you to wrap a command and show the wizard mode based on a predicate.

@IMax153 good feedback. Re-implemented it in this way. Let me know what you think?

ryanbas21 avatar Nov 08 '25 00:11 ryanbas21