camunda / camunda/c8ctl

Interactive disambiguation of user intent

Open
#409 1 comment 0 reactions 1 assignee View on GitHub

@emilyoram is already working on this.

Since May 25, 2026.

enhancement
Dominant language
TypeScript
Stars
7
Forks
0
Avg merge
15h 36m
Merged PRs (30d)
28

Description

Problem

Two user-reported issues highlight the same root problem — c8ctl cannot always determine what the user intends, and when it guesses wrong the consequences are costly:

  1. Deploying to the wrong cluster (https://github.com/camunda/c8ctl/issues/393) — When multiple profiles are configured, c8 deploy silently uses whichever profile happens to be active. A user who forgot to c8 use profile deploys to production instead of staging.

  2. Deploy includes unexpected files (https://github.com/camunda/c8ctl/issues/350) — The expanded resource deployment candidate set in Camunda 8.10 changes which file extensions are deployable. A directory that previously deployed only .bpmn files now picks up .md, .json, .yaml, etc. The determination surface differs between server versions, degrading certainty about what will be deployed.

Both issues are about disambiguating user intent — the CLI needs to know with certainty where the user wants to deploy and what they want to deploy.

Proposal: optional interactive disambiguation

When ambiguity exists and the terminal is interactive (TTY), c8ctl should ask the user rather than guess or bail with a complex message. The interactive prompts clarify intent at the moment it matters, with options to persist the choice so the user is not asked repeatedly.

Power users can disable this mode, and it is suppressed in non-interactive CI.

A working prototype of this UX pattern is on the poc/interactive-menu branch. Video demo.

Disambiguation point 1: deploy target (which cluster?)

When multiple profiles are configured and the user did not explicitly specify a target (--profile or --yes), present an arrow-key selector:

? Which profile do you want to deploy to?
  ❯ staging   https://staging.example.com
    production https://production.example.com
    local      http://localhost:8080/v2

The currently active profile is pre-selected. The user confirms with Enter or cancels with Escape.

Disambiguation point 2: skipped files (which resources?)

When a directory scan skips files because their extensions are outside the default allow-list (.bpmn, .dmn, .form), present the skipped files and a menu of actions:

Found 3 file(s) with extensions not in the allow-list (.md, .json):
  • readme.md
  • config.json
  • subdir/docs.md

? What do you want to do about these files?
  ❯ Deploy them              include in this deployment only
    Ignore them              skip for now
    Deploy them always       remember these files in .c8ignore
    Ignore them always       remember to skip these files in .c8ignore
    Show me how to configure this

"Deploy always" and "Ignore always" persist the choice to .c8ignore so the user is not prompted again.

Disambiguation point 3: profile selection for use and remove

When c8 use profile or c8 remove profile is invoked without a name argument, present an interactive picker instead of erroring:

? Which profile do you want to use?
  ❯ staging       https://staging.example.com
    production    https://production.example.com (Modeler)
    local         http://localhost:8080/v2

remove profile additionally confirms before deleting:

? Remove profile 'staging'? [y/N]

Non-interactive fallback

All prompts degrade gracefully when stdin or stderr is not a TTY (CI, pipes, scripts):

  • Deploy target: auto-approves and logs the target to stderr.
  • Skipped files: silently ignores (preserves current behavior).
  • Profile pickers: throws with a usage hint (c8 use profile <name>).

--yes (-y) is a global flag that suppresses all interactive prompts, equivalent to non-interactive mode.

Design principles (from the prototype)

  • Zero external dependencies — uses Node's built-in readline and ANSI escape codes. No inquirer, prompts, or similar.
  • stderr for prompts, stdout for data — interactive menus write to stderr, keeping stdout clean for piped JSON/table output.
  • TTY detectionprocess.stdin.isTTY && process.stderr.isTTY gates all interactive behavior.
  • select() and confirm() primitives — reusable across any command that needs disambiguation. Exposed to plugins via PluginCtx.prompt.
  • Pre-flight server version checkdeploy checks the Camunda version before resource collection so the skipped-files menu correctly reflects what the server accepts (8.10+ supports extended extensions).

Alternatives evaluated

  1. Bail with a help hint — print the combination of flags needed to establish certainty and exit non-zero. This forces the user to re-run the command. Rejected because it adds friction without guiding the choice.
  2. Emit a message telling the user how to persist their preference — log a hint after the operation. This doesn't prevent the mistake — the deploy has already happened. Rejected because the disambiguation needs to happen before the side effect.

Scope

The prototype on poc/interactive-menu implements all three disambiguation points along with the --yes global flag, TTY detection, deploy confirmation guard, and tests. It is a proof-of-concept for the UX pattern, not a final implementation. Work items to productionize:

  • Extract select() / confirm() into their own PR (framework primitives)
  • Deploy target confirmation (interactive profile picker + --yes flag)
  • Skipped-files interactive menu (with .c8ignore persistence and server version check)
  • Interactive profile pickers for use profile / remove profile
  • Shell completion for --yes / -y
  • Documentation updates (README, EXAMPLES.md, command reference)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.