Skip to content

Conversation

@ryanbas21
Copy link

@ryanbas21 ryanbas21 commented Nov 7, 2025

Type

  • Refactor
  • 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

@ryanbas21 ryanbas21 requested a review from IMax153 as a code owner November 7, 2025 03:30
@github-project-automation github-project-automation bot moved this to Discussion Ongoing in PR Backlog Nov 7, 2025
@changeset-bot
Copy link

changeset-bot bot commented Nov 7, 2025

🦋 Changeset detected

Latest commit: 7e62945

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

@ryanbas21 ryanbas21 force-pushed the feat/programmatic-wizard-mode branch 3 times, most recently from 3a0ff98 to 40d6e12 Compare November 7, 2025 03:33
@IMax153
Copy link
Member

IMax153 commented Nov 7, 2025

@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.

@ryanbas21 ryanbas21 force-pushed the feat/programmatic-wizard-mode branch from 40d6e12 to 1ad7506 Compare November 8, 2025 00:37
@ryanbas21 ryanbas21 changed the title feat(cli): add Command.runWithWizard for programmatic wizard mode feat(cli): add Command.withConditionalBehavior for predicate based wrapping of commands Nov 8, 2025
@ryanbas21
Copy link
Author

@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 ryanbas21 force-pushed the feat/programmatic-wizard-mode branch 2 times, most recently from 5bc8ffb to db385a7 Compare November 8, 2025 01:00
…mand behaviors

Introduces a composable combinator approach for adding conditional behaviors to commands.
Instead of having separate runWith* methods, this provides a single, extensible pattern.

Key features:
- Command.withConditionalBehavior() wraps commands with conditional behavior
- Predicate function determines when behavior should trigger
- Supports built-in "wizard" mode or custom behavior functions
- Fully composable with other Command combinators via .pipe()
- Backwards compatible with existing --wizard flag

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

Command.run(command, { name: "MyApp", version: "1.0.0" })
```

This design avoids API explosion (runWithWizard, runWithHelp, etc.) and follows
functional composition patterns. The same combinator can be extended for other
conditional behaviors like help display, validation, or custom prompting.

Fixes Effect-TS#5699

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ryanbas21 ryanbas21 force-pushed the feat/programmatic-wizard-mode branch from db385a7 to 7e62945 Compare November 8, 2025 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Discussion Ongoing

Development

Successfully merging this pull request may close these issues.

@effect/cli Programatically invoke wizard mode

2 participants