feat(cli): improve interactive prompt UI with @clack/prompts#1370
feat(cli): improve interactive prompt UI with @clack/prompts#1370mrgrain wants to merge 7 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1370 +/- ##
==========================================
- Coverage 88.31% 88.20% -0.12%
==========================================
Files 74 74
Lines 10430 10458 +28
Branches 1427 1432 +5
==========================================
+ Hits 9211 9224 +13
- Misses 1191 1206 +15
Partials 28 28
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
🐝 is using @clack/prompts the best thing to do here? |
🐝 The Hive Has SpokenQuestion: "is using @clack/prompts the best thing to do here?" Options considered:
Winner: Custom Prompt System (1 round)
Debate detailsRound 1: Custom Prompt System(25) · Hybrid Prompt Strategy(19) · Keep @clack/prompts(16) · Ink React CLI(0) · Terminal UI Framework(0) |
🐝 The Hive Has SpokenQuestion: "is using @clack/prompts the best thing to do here?" Options considered:
Winner: Custom Prompt System (1 round)
Debate detailsRound 1: Custom Prompt System(30) · Hybrid Prompt Strategy(20) · Keep @clack/prompts(10) · Ink React CLI(0) · Terminal UI Framework(0) |
| public append(chunk: string): void { | ||
| const lines = chunk.split(os.EOL); | ||
| // Strip ANSI escape codes so prompt matching works regardless of terminal styling | ||
| const clean = chunk.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, ''); |
There was a problem hiding this comment.
Extracting this to a function might be nice, then we don't need the comment anymore.
| // Split on \r, \n, or \r\n — interactive prompt libraries like @clack/prompts | ||
| // use \r and cursor movement instead of plain \n for re-rendering |
There was a problem hiding this comment.
I believe that they expect only \r as input for ENTER, but this is about output, isn't it? Is this actually true?
| module.exports = { | ||
| ConfirmPrompt: jest.fn().mockImplementation(() => ({ prompt: jest.fn().mockResolvedValue(true) })), | ||
| TextPrompt: jest.fn().mockImplementation(() => ({ prompt: jest.fn().mockResolvedValue('') })), | ||
| SelectPrompt: jest.fn().mockImplementation(() => ({ prompt: mockPrompt })), | ||
| isCancel: jest.fn().mockReturnValue(false), |
There was a problem hiding this comment.
Is there a way to turn this into a fake rather than a mock?
| // @clack/core is ESM-only and cannot be loaded by Jest in CJS mode. | ||
| // Map it to a manual mock that provides safe defaults for all tests. | ||
| moduleNameMapper: { |
There was a problem hiding this comment.
If this is true, then how does it work for production code? All our code is CJS, so how do we load an ESM module then? It's only in recent Node versions that CJS can finally directly require() ESM, and I didn't see an await import() ?
Improves the interactive CLI experience for deploy approvals, text prompts, and the flags command by adopting
@clack/prompts— a modern, actively maintained prompt library with beautiful default styling.The CLI previously relied on
promptlyandenquirerfor user interaction, both of which are unmaintained and produce plain, unstyled output.@clack/promptsprovides a consistent, visually polished prompt experience out of the box, with clear confirmation dialogs, styled select menus, and proper cancellation handling via Ctrl+C.Beyond the immediate UI improvements, this also lays the groundwork for richer CLI interactions in the future.
@clack/promptsships withspinner,taskLog, and structured logging utilities that can be used to improve deploy progress, asset upload feedback, and other long-running operations.Before / After
Deploy approval
Before
After
Flags interactive menu
Before
After
Text input
Before
After
Since
@clack/promptsis an ESM-only package and Jest runs in CJS mode, a global mock file is provided attest/_helpers/mock-clack-prompts.js. This is appropriate because prompt functions are inherently interactive and should always be mocked in tests. The bundled CLI binary is unaffected since esbuild handles ESM→CJS interop at build time.Fixes #
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license