feat: migrate prompts to Clack#305
Conversation
…s-migration # Conflicts: # bun.lock # packages/cli-core/package.json # packages/cli-core/src/commands/api/interactive.test.ts # packages/cli-core/src/commands/apps/list.test.ts # packages/cli-core/src/commands/deploy/index.test.ts # packages/cli-core/src/commands/orgs/index.test.ts # packages/cli-core/src/commands/unlink/index.test.ts # packages/cli-core/src/commands/users/create.test.ts # packages/cli-core/src/commands/whoami/index.ts # packages/cli-core/src/lib/listage.test.ts # packages/cli-core/src/lib/spinner.ts # packages/cli-core/src/test/lib/stubs.ts
🦋 Changeset detectedLatest commit: a4b1990 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR migrates the CLI from Inquirer-based prompts to Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/users/create-wizard.ts`:
- Around line 109-110: text() can return undefined (validator accepts string |
undefined) so calling value.trim() may throw; update the handling around the
text({ message, validate }) call in create-wizard.ts: either coalesce undefined
before trimming (e.g., treat undefined as empty string) or explicitly guard and
return undefined when value is undefined (respecting the required flag). Locate
the call to text(), the validate function and the required logic, and replace
the raw value.trim() with a safe branch that handles undefined (using a
null-coalescing or an if-check) so trim() is only invoked on a string.
In `@packages/cli-core/src/commands/users/list.ts`:
- Around line 164-207: The intro()/outro() calls in list() are not paired when
the JSON fast-path returns early; ensure intro is only called when not in JSON
mode or always call outro before any early return: check the nested flag and the
printJson({ data: users, hasMore }, options) result, and either move intro() to
after the printJson check or invoke outro() before returning from the JSON
fast-path; update the list function (symbols: list, nested, intro, outro,
printJson) so that no execution path returns after intro() without calling
outro(), and apply the same pattern to other affected commands (apps/create and
config/push) where intro/outro lifecycle may be unpaired.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 66af935c-b709-463a-b959-39dc9d709ab0
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (64)
.changeset/clack-prompts-migration.md.claude/rules/logging.mdpackage.jsonpackages/cli-core/package.jsonpackages/cli-core/src/cli-program.tspackages/cli-core/src/commands/api/index.test.tspackages/cli-core/src/commands/api/index.tspackages/cli-core/src/commands/api/interactive.test.tspackages/cli-core/src/commands/api/interactive.tspackages/cli-core/src/commands/apps/create.test.tspackages/cli-core/src/commands/apps/create.tspackages/cli-core/src/commands/apps/list.test.tspackages/cli-core/src/commands/apps/list.tspackages/cli-core/src/commands/auth/login.test.tspackages/cli-core/src/commands/auth/login.tspackages/cli-core/src/commands/auth/logout.tspackages/cli-core/src/commands/billing/index.test.tspackages/cli-core/src/commands/billing/index.tspackages/cli-core/src/commands/config/pull.test.tspackages/cli-core/src/commands/config/pull.tspackages/cli-core/src/commands/config/push.test.tspackages/cli-core/src/commands/config/push.tspackages/cli-core/src/commands/config/schema.tspackages/cli-core/src/commands/deploy/index.test.tspackages/cli-core/src/commands/deploy/index.tspackages/cli-core/src/commands/doctor/index.tspackages/cli-core/src/commands/env/pull.test.tspackages/cli-core/src/commands/env/pull.tspackages/cli-core/src/commands/init/bootstrap.tspackages/cli-core/src/commands/init/index.tspackages/cli-core/src/commands/link/index.test.tspackages/cli-core/src/commands/link/index.tspackages/cli-core/src/commands/open/index.tspackages/cli-core/src/commands/orgs/index.test.tspackages/cli-core/src/commands/orgs/index.tspackages/cli-core/src/commands/skill/install.tspackages/cli-core/src/commands/switch-env/index.test.tspackages/cli-core/src/commands/switch-env/index.tspackages/cli-core/src/commands/unlink/index.test.tspackages/cli-core/src/commands/unlink/index.tspackages/cli-core/src/commands/update/index.tspackages/cli-core/src/commands/users/create-wizard.test.tspackages/cli-core/src/commands/users/create-wizard.tspackages/cli-core/src/commands/users/create.test.tspackages/cli-core/src/commands/users/create.tspackages/cli-core/src/commands/users/list.test.tspackages/cli-core/src/commands/users/list.tspackages/cli-core/src/commands/users/menu.test.tspackages/cli-core/src/commands/users/menu.tspackages/cli-core/src/commands/users/open.tspackages/cli-core/src/lib/app-picker.tspackages/cli-core/src/lib/listage.test.tspackages/cli-core/src/lib/listage.tspackages/cli-core/src/lib/prompts.test.tspackages/cli-core/src/lib/prompts.tspackages/cli-core/src/lib/spinner.test.tspackages/cli-core/src/lib/spinner.tspackages/cli-core/src/lib/ui.tspackages/cli-core/src/test/integration/lib/harness.tspackages/cli-core/src/test/integration/users-commands.test.tspackages/cli-core/src/test/lib/listage-stubs.tspackages/cli-core/src/test/lib/stubs.tstest/e2e/fixtures/tanstack-start/README.mdtest/e2e/fixtures/tanstack-start/src/styles.css
💤 Files with no reviewable changes (2)
- packages/cli-core/src/test/lib/listage-stubs.ts
- test/e2e/fixtures/tanstack-start/src/styles.css
👮 Files not reviewed due to content moderation or server errors (8)
- packages/cli-core/src/commands/users/menu.ts
- .claude/rules/logging.md
- packages/cli-core/src/commands/users/create-wizard.test.ts
- packages/cli-core/package.json
- packages/cli-core/src/commands/unlink/index.ts
- packages/cli-core/src/commands/auth/logout.ts
- packages/cli-core/src/commands/orgs/index.test.ts
- packages/cli-core/src/commands/api/index.test.ts
| const value = await text({ message, validate }); | ||
| return value.trim(); |
There was a problem hiding this comment.
Potential runtime error: .trim() called on possibly-undefined value.
The validator type signature on line 104 accepts string | undefined, indicating that text() may return undefined. When required is false (line 105), no validation runs, so text() could return undefined, causing line 110 to throw when calling .trim().
🐛 Proposed fix: add null-coalescing or type guard
const value = await text({ message, validate });
- return value.trim();
+ return value?.trim() ?? "";Alternatively, if empty strings should not be returned for optional fields, handle undefined explicitly:
const value = await text({ message, validate });
- return value.trim();
+ if (!value) return "";
+ return value.trim();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const value = await text({ message, validate }); | |
| return value.trim(); | |
| const value = await text({ message, validate }); | |
| return value?.trim() ?? ""; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli-core/src/commands/users/create-wizard.ts` around lines 109 -
110, text() can return undefined (validator accepts string | undefined) so
calling value.trim() may throw; update the handling around the text({ message,
validate }) call in create-wizard.ts: either coalesce undefined before trimming
(e.g., treat undefined as empty string) or explicitly guard and return undefined
when value is undefined (respecting the required flag). Locate the call to
text(), the validate function and the required logic, and replace the raw
value.trim() with a safe branch that handles undefined (using a null-coalescing
or an if-check) so trim() is only invoked on a string.
There was a problem hiding this comment.
packages/cli-core/src/lib/prompts.ts types text() as Promise<string> after unwrap() converts Clack cancel symbols into UserAbortError. The string | undefined type is only for the validate callback input, matching Clack’s validator contract, not the prompt result. Empty optional fields come back as strings, so .trim() is safe here. Am I missing a runtime path where our wrapper returns undefined?
There was a problem hiding this comment.
@wyattjoh, you're right — I apologize for the noise. The AI summary for prompts.ts confirms that text() is typed as returning Promise<string>, with string | undefined scoped only to the validate callback's parameter to match Clack's validator contract. Since unwrap() converts the cancel symbol to UserAbortError, undefined can never reach the call site and .trim() is completely safe here. My concern was unfounded.
✏️ Learnings added
Learnt from: wyattjoh
Repo: clerk/cli PR: 305
File: packages/cli-core/src/commands/users/create-wizard.ts:109-110
Timestamp: 2026-05-21T21:11:51.464Z
Learning: In `packages/cli-core/src/lib/prompts.ts`, the exported `text()`, `password()`, and `editor()` functions all return `Promise<string>` (never `undefined`). The `unwrap()` helper converts Clack's cancel symbol into a `UserAbortError` exception. The `string | undefined` type in validate callbacks is Clack's validator contract for the *input parameter*, not the return type of the prompt. Callers can safely call `.trim()` on the resolved value without a null guard.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
- pair users list intro/outro around human output only - avoid app create UI wrapping in JSON and agent output - ensure config push closes intro/outro across early exits
What
@clack/prompts.@inquirer/*dependencies and updates prompt-related tests and fixtures.Why
This moves the CLI to the new prompt UI model while keeping agent and machine-readable output paths stable.
Testing
bun run typecheckbun run format:checkbun run test