Skip to content

feat: migrate prompts to Clack#305

Open
wyattjoh wants to merge 33 commits into
mainfrom
wyattjoh/clack-prompts-migration
Open

feat: migrate prompts to Clack#305
wyattjoh wants to merge 33 commits into
mainfrom
wyattjoh/clack-prompts-migration

Conversation

@wyattjoh
Copy link
Copy Markdown
Contributor

What

  • Migrates CLI prompt wrappers, list selection, spinners, and human-facing command output to @clack/prompts.
  • Routes command input through shared prompt helpers and adds Clack UI output capture for tests.
  • Removes direct @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 typecheck
  • bun run format:check
  • bun run test

wyattjoh added 30 commits May 12, 2026 12:29
wyattjoh added 2 commits May 21, 2026 14:10
…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-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 21, 2026

🦋 Changeset detected

Latest commit: a4b1990

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

This PR includes changesets to release 1 package
Name Type
clerk 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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5544b8df-8ce1-4538-b1dd-f13d396ebfce

📥 Commits

Reviewing files that changed from the base of the PR and between 8ab8957 and a4b1990.

📒 Files selected for processing (3)
  • packages/cli-core/src/commands/apps/create.ts
  • packages/cli-core/src/commands/config/push.ts
  • packages/cli-core/src/commands/users/list.ts

📝 Walkthrough

Walkthrough

This PR migrates the CLI from Inquirer-based prompts to @clack/prompts, centralizes UI output via a new ui module, refactors list prompts (select/search) and spinner behavior to use Clack, updates package dependencies, and integrates intro/outro framing and prompt-wrapper usage across commands and tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • clerk/cli#287: Updates CLI logging/UI plumbing by refactoring spinner output and log capture, directly preparing the foundation for this prompt migration.
  • clerk/cli#280: Refactors CLI UI/logging infrastructure toward shared log.ui() streaming, complementary plumbing work for unified UI output control.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: migrate prompts to Clack' accurately and concisely summarizes the main change: migrating the CLI's prompt system from Inquirer to Clack.
Description check ✅ Passed The description clearly relates to the changeset, explaining the migration to @clack/prompts, removal of @inquirer dependencies, and routing updates while noting stable agent/machine output.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a47f50 and 8ab8957.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (64)
  • .changeset/clack-prompts-migration.md
  • .claude/rules/logging.md
  • package.json
  • packages/cli-core/package.json
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/commands/api/index.test.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/commands/api/interactive.test.ts
  • packages/cli-core/src/commands/api/interactive.ts
  • packages/cli-core/src/commands/apps/create.test.ts
  • packages/cli-core/src/commands/apps/create.ts
  • packages/cli-core/src/commands/apps/list.test.ts
  • packages/cli-core/src/commands/apps/list.ts
  • packages/cli-core/src/commands/auth/login.test.ts
  • packages/cli-core/src/commands/auth/login.ts
  • packages/cli-core/src/commands/auth/logout.ts
  • packages/cli-core/src/commands/billing/index.test.ts
  • packages/cli-core/src/commands/billing/index.ts
  • packages/cli-core/src/commands/config/pull.test.ts
  • packages/cli-core/src/commands/config/pull.ts
  • packages/cli-core/src/commands/config/push.test.ts
  • packages/cli-core/src/commands/config/push.ts
  • packages/cli-core/src/commands/config/schema.ts
  • packages/cli-core/src/commands/deploy/index.test.ts
  • packages/cli-core/src/commands/deploy/index.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/env/pull.test.ts
  • packages/cli-core/src/commands/env/pull.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/index.ts
  • packages/cli-core/src/commands/link/index.test.ts
  • packages/cli-core/src/commands/link/index.ts
  • packages/cli-core/src/commands/open/index.ts
  • packages/cli-core/src/commands/orgs/index.test.ts
  • packages/cli-core/src/commands/orgs/index.ts
  • packages/cli-core/src/commands/skill/install.ts
  • packages/cli-core/src/commands/switch-env/index.test.ts
  • packages/cli-core/src/commands/switch-env/index.ts
  • packages/cli-core/src/commands/unlink/index.test.ts
  • packages/cli-core/src/commands/unlink/index.ts
  • packages/cli-core/src/commands/update/index.ts
  • packages/cli-core/src/commands/users/create-wizard.test.ts
  • packages/cli-core/src/commands/users/create-wizard.ts
  • packages/cli-core/src/commands/users/create.test.ts
  • packages/cli-core/src/commands/users/create.ts
  • packages/cli-core/src/commands/users/list.test.ts
  • packages/cli-core/src/commands/users/list.ts
  • packages/cli-core/src/commands/users/menu.test.ts
  • packages/cli-core/src/commands/users/menu.ts
  • packages/cli-core/src/commands/users/open.ts
  • packages/cli-core/src/lib/app-picker.ts
  • packages/cli-core/src/lib/listage.test.ts
  • packages/cli-core/src/lib/listage.ts
  • packages/cli-core/src/lib/prompts.test.ts
  • packages/cli-core/src/lib/prompts.ts
  • packages/cli-core/src/lib/spinner.test.ts
  • packages/cli-core/src/lib/spinner.ts
  • packages/cli-core/src/lib/ui.ts
  • packages/cli-core/src/test/integration/lib/harness.ts
  • packages/cli-core/src/test/integration/users-commands.test.ts
  • packages/cli-core/src/test/lib/listage-stubs.ts
  • packages/cli-core/src/test/lib/stubs.ts
  • test/e2e/fixtures/tanstack-start/README.md
  • test/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

Comment on lines +109 to 110
const value = await text({ message, validate });
return value.trim();
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

Suggested change
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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread packages/cli-core/src/commands/users/list.ts
- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant