Improve setup UX#32
Conversation
📝 WalkthroughWalkthroughPromotes ChangesPrimary Onboarding Commands & Workflow Help
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (1 warning, 2 inconclusive)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/cli/commands/setup.ts (1)
107-128: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winTighten the
--agentguidance branch.This currently tells users how to use the adapter even on
--dry-run, and on realsetup --agent ...runs it printsrevpack preparetwice.Suggested fix
- if (opts.agent) { + if (opts.agent && !opts.dryRun) { printAgentUsage(opts.agent); } @@ - if (opts.agent) { - console.log(formatGuidanceLine(' revpack prepare')); - } else if (!opts.prompts) { + if (!opts.agent && !opts.prompts) { console.log(formatGuidanceLine(' Tip: install an agent adapter, for example:')); console.log(formatGuidanceLine(' revpack setup agent codex')); console.log(formatGuidanceLine(' Or create both files at once:')); console.log(formatGuidanceLine(' revpack setup --agent codex')); - } else { + } else if (!opts.agent) { console.log(formatGuidanceLine(' Tip: `revpack setup --prompts` is deprecated; use:')); console.log(formatGuidanceLine(' revpack setup agent copilot')); } console.log(formatGuidanceLine(' revpack prepare'));🤖 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 `@src/cli/commands/setup.ts` around lines 107 - 128, The `setup` command’s guidance output in `printAgentUsage`/the `opts.agent` branch is too broad and duplicates `revpack prepare`. Update the logic so adapter usage help is shown only for real setup runs (not `--dry-run`), and ensure the `revpack prepare` guidance is emitted only once when `opts.agent` is set. Keep the `results.some(...)` gating and adjust the `opts.agent`, `opts.prompts`, and default branches accordingly.
🤖 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 `@src/cli/commands/config.ts`:
- Around line 353-362: The remote URL normalization in config handling does not
treat ssh:// remotes as provider HTTPS URLs, so they fall through the URL
parsing branch and keep the ssh scheme. Update the remote parsing logic in the
config command’s URL suggestion path to recognize ssh://git@host/org/repo.git
alongside the existing SSH regex case and convert it to an https://host-style
suggestion, using the same `suggestedUrl` and `suggestedName` logic before
`normalizeProviderUrlInput()` is called.
- Around line 399-450: The profile creation flow in the config command is
leaving `providerUrl` empty for `bitbucket-cloud`, which breaks validation and
remote matching. Update the prompt/assignment logic around `provider`, `url`,
and `RevpackProfile` so that when the selected provider is `bitbucket-cloud` and
the URL input is blank, it defaults to `https://bitbucket.org` before building
the profile. Make sure this happens in the same path that currently derives
`defaultName`, `extraPatternPrompt`, and the final `profile` object so
`runDoctor()` and remote matching continue to work.
---
Outside diff comments:
In `@src/cli/commands/setup.ts`:
- Around line 107-128: The `setup` command’s guidance output in
`printAgentUsage`/the `opts.agent` branch is too broad and duplicates `revpack
prepare`. Update the logic so adapter usage help is shown only for real setup
runs (not `--dry-run`), and ensure the `revpack prepare` guidance is emitted
only once when `opts.agent` is set. Keep the `results.some(...)` gating and
adjust the `opts.agent`, `opts.prompts`, and default branches accordingly.
🪄 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: 557bb2dd-4e1f-435a-83fa-72a11b338009
📒 Files selected for processing (12)
CHANGELOG.mdREADME.mddocs/commands.mdsrc/cli/commands/checkout.test.tssrc/cli/commands/checkout.tssrc/cli/commands/config.test.tssrc/cli/commands/config.tssrc/cli/commands/setup.test.tssrc/cli/commands/setup.tssrc/cli/index.tssrc/config/index.tssrc/config/profile-resolver.ts
| try { | ||
| // Handle SSH URLs like git@host:group/project.git | ||
| const sshMatch = remoteUrl.match(/@([^:]+):/); | ||
| if (sshMatch) { | ||
| suggestedUrl = `https://${sshMatch[1]}`; | ||
| suggestedName = sshMatch[1].split('.')[0]; | ||
| } else { | ||
| const parsed = new URL(remoteUrl); | ||
| suggestedUrl = `${parsed.protocol}//${parsed.host}`; | ||
| suggestedName = parsed.hostname.split('.')[0]; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Normalize ssh:// remotes to an HTTPS provider URL.
ssh://git@host/org/repo.git remotes land in the new URL(remoteUrl) branch and currently produce a suggested default like ssh://host. Accepting that default fails immediately because normalizeProviderUrlInput() only accepts http/https.
Suggested fix
} else {
const parsed = new URL(remoteUrl);
- suggestedUrl = `${parsed.protocol}//${parsed.host}`;
+ const suggestedProtocol = parsed.protocol === 'ssh:' ? 'https:' : parsed.protocol;
+ suggestedUrl = `${suggestedProtocol}//${parsed.host}`;
suggestedName = parsed.hostname.split('.')[0];
}📝 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.
| try { | |
| // Handle SSH URLs like git@host:group/project.git | |
| const sshMatch = remoteUrl.match(/@([^:]+):/); | |
| if (sshMatch) { | |
| suggestedUrl = `https://${sshMatch[1]}`; | |
| suggestedName = sshMatch[1].split('.')[0]; | |
| } else { | |
| const parsed = new URL(remoteUrl); | |
| suggestedUrl = `${parsed.protocol}//${parsed.host}`; | |
| suggestedName = parsed.hostname.split('.')[0]; | |
| try { | |
| // Handle SSH URLs like git@host:group/project.git | |
| const sshMatch = remoteUrl.match(/@([^:]+):/); | |
| if (sshMatch) { | |
| suggestedUrl = `https://${sshMatch[1]}`; | |
| suggestedName = sshMatch[1].split('.')[0]; | |
| } else { | |
| const parsed = new URL(remoteUrl); | |
| const suggestedProtocol = parsed.protocol === 'ssh:' ? 'https:' : parsed.protocol; | |
| suggestedUrl = `${suggestedProtocol}//${parsed.host}`; | |
| suggestedName = parsed.hostname.split('.')[0]; |
🤖 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 `@src/cli/commands/config.ts` around lines 353 - 362, The remote URL
normalization in config handling does not treat ssh:// remotes as provider HTTPS
URLs, so they fall through the URL parsing branch and keep the ssh scheme.
Update the remote parsing logic in the config command’s URL suggestion path to
recognize ssh://git@host/org/repo.git alongside the existing SSH regex case and
convert it to an https://host-style suggestion, using the same `suggestedUrl`
and `suggestedName` logic before `normalizeProviderUrlInput()` is called.
| const defaultProvider = getSetupProviderDefault(url, detectedProvider); | ||
| const providerInput = shouldPromptForSetupProvider(url) | ||
| ? (await ask(`Provider (gitlab/github/bitbucket-cloud) [${defaultProvider}]: `)) || defaultProvider | ||
| : defaultProvider; | ||
| let provider: ProviderType; | ||
| try { | ||
| provider = normalizeProviderInput(providerInput); | ||
| validateProviderUrlForProvider(url, provider); | ||
| } catch (err) { | ||
| rl.close(); | ||
| throw err; | ||
| } | ||
| const defaultName = deriveProfileNameFromProviderUrl(url) || suggestedName || 'default'; | ||
| const name = (await ask(`Profile name [${defaultName}]: `)) || defaultName; | ||
| const defaultTokenEnv = getDefaultTokenEnv(provider); | ||
| const tokenEnv = (await ask(`Token environment variable [${defaultTokenEnv}]: `)) || defaultTokenEnv; | ||
| let emailEnv = ''; | ||
| if (provider === 'bitbucket-cloud') { | ||
| emailEnv = | ||
| (await ask(`Atlassian account email environment variable [REVPACK_BITBUCKET_EMAIL]: `)) || | ||
| 'REVPACK_BITBUCKET_EMAIL'; | ||
| } | ||
|
|
||
| // Derive host from URL for matching info | ||
| let derivedHost = ''; | ||
| if (url) { | ||
| try { | ||
| derivedHost = new URL(url).host; | ||
| } catch { | ||
| /* ignore */ | ||
| } | ||
| } | ||
| const extraPatternPrompt = derivedHost ? `Additional remote match pattern (optional): ` : `Remote match pattern: `; | ||
| const extraPattern = await ask(extraPatternPrompt); | ||
|
|
||
| let caFileInput = ''; | ||
| let tlsInput = 'yes'; | ||
| let sshCloneInput = 'no'; | ||
| const isCloudProvider = isManagedCloudProvider(url, provider); | ||
| if (!isCloudProvider) { | ||
| caFileInput = await ask(`Custom CA file (optional): `); | ||
| tlsInput = (await ask(`Verify TLS certificates [yes]: `)) || 'yes'; | ||
| sshCloneInput = (await ask(`Use SSH for git clone (revpack checkout) [no]: `)) || 'no'; | ||
| } | ||
|
|
||
| rl.close(); | ||
|
|
||
| const profile: RevpackProfile = { | ||
| provider, | ||
| }; | ||
| if (url) profile.url = url; | ||
| if (tokenEnv) profile.tokenEnv = tokenEnv; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Default Bitbucket Cloud to https://bitbucket.org when URL is left blank.
If the user selects bitbucket-cloud and accepts an empty provider URL, this saves a profile with no url. That breaks parity with config profile create, leaves runDoctor() reporting the profile invalid, and removes the URL-derived remote match.
Suggested fix
let provider: ProviderType;
try {
provider = normalizeProviderInput(providerInput);
+ if (!url && provider === 'bitbucket-cloud') {
+ url = 'https://bitbucket.org';
+ }
validateProviderUrlForProvider(url, provider);
} catch (err) {
rl.close();
throw err;
}📝 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 defaultProvider = getSetupProviderDefault(url, detectedProvider); | |
| const providerInput = shouldPromptForSetupProvider(url) | |
| ? (await ask(`Provider (gitlab/github/bitbucket-cloud) [${defaultProvider}]: `)) || defaultProvider | |
| : defaultProvider; | |
| let provider: ProviderType; | |
| try { | |
| provider = normalizeProviderInput(providerInput); | |
| validateProviderUrlForProvider(url, provider); | |
| } catch (err) { | |
| rl.close(); | |
| throw err; | |
| } | |
| const defaultName = deriveProfileNameFromProviderUrl(url) || suggestedName || 'default'; | |
| const name = (await ask(`Profile name [${defaultName}]: `)) || defaultName; | |
| const defaultTokenEnv = getDefaultTokenEnv(provider); | |
| const tokenEnv = (await ask(`Token environment variable [${defaultTokenEnv}]: `)) || defaultTokenEnv; | |
| let emailEnv = ''; | |
| if (provider === 'bitbucket-cloud') { | |
| emailEnv = | |
| (await ask(`Atlassian account email environment variable [REVPACK_BITBUCKET_EMAIL]: `)) || | |
| 'REVPACK_BITBUCKET_EMAIL'; | |
| } | |
| // Derive host from URL for matching info | |
| let derivedHost = ''; | |
| if (url) { | |
| try { | |
| derivedHost = new URL(url).host; | |
| } catch { | |
| /* ignore */ | |
| } | |
| } | |
| const extraPatternPrompt = derivedHost ? `Additional remote match pattern (optional): ` : `Remote match pattern: `; | |
| const extraPattern = await ask(extraPatternPrompt); | |
| let caFileInput = ''; | |
| let tlsInput = 'yes'; | |
| let sshCloneInput = 'no'; | |
| const isCloudProvider = isManagedCloudProvider(url, provider); | |
| if (!isCloudProvider) { | |
| caFileInput = await ask(`Custom CA file (optional): `); | |
| tlsInput = (await ask(`Verify TLS certificates [yes]: `)) || 'yes'; | |
| sshCloneInput = (await ask(`Use SSH for git clone (revpack checkout) [no]: `)) || 'no'; | |
| } | |
| rl.close(); | |
| const profile: RevpackProfile = { | |
| provider, | |
| }; | |
| if (url) profile.url = url; | |
| if (tokenEnv) profile.tokenEnv = tokenEnv; | |
| const defaultProvider = getSetupProviderDefault(url, detectedProvider); | |
| const providerInput = shouldPromptForSetupProvider(url) | |
| ? (await ask(`Provider (gitlab/github/bitbucket-cloud) [${defaultProvider}]: `)) || defaultProvider | |
| : defaultProvider; | |
| let provider: ProviderType; | |
| try { | |
| provider = normalizeProviderInput(providerInput); | |
| if (!url && provider === 'bitbucket-cloud') { | |
| url = 'https://bitbucket.org'; | |
| } | |
| validateProviderUrlForProvider(url, provider); | |
| } catch (err) { | |
| rl.close(); | |
| throw err; | |
| } | |
| const defaultName = deriveProfileNameFromProviderUrl(url) || suggestedName || 'default'; | |
| const name = (await ask(`Profile name [${defaultName}]: `)) || defaultName; | |
| const defaultTokenEnv = getDefaultTokenEnv(provider); | |
| const tokenEnv = (await ask(`Token environment variable [${defaultTokenEnv}]: `)) || defaultTokenEnv; | |
| let emailEnv = ''; | |
| if (provider === 'bitbucket-cloud') { | |
| emailEnv = | |
| (await ask(`Atlassian account email environment variable [REVPACK_BITBUCKET_EMAIL]: `)) || | |
| 'REVPACK_BITBUCKET_EMAIL'; | |
| } | |
| // Derive host from URL for matching info | |
| let derivedHost = ''; | |
| if (url) { | |
| try { | |
| derivedHost = new URL(url).host; | |
| } catch { | |
| /* ignore */ | |
| } | |
| } | |
| const extraPatternPrompt = derivedHost ? `Additional remote match pattern (optional): ` : `Remote match pattern: `; | |
| const extraPattern = await ask(extraPatternPrompt); | |
| let caFileInput = ''; | |
| let tlsInput = 'yes'; | |
| let sshCloneInput = 'no'; | |
| const isCloudProvider = isManagedCloudProvider(url, provider); | |
| if (!isCloudProvider) { | |
| caFileInput = await ask(`Custom CA file (optional): `); | |
| tlsInput = (await ask(`Verify TLS certificates [yes]: `)) || 'yes'; | |
| sshCloneInput = (await ask(`Use SSH for git clone (revpack checkout) [no]: `)) || 'no'; | |
| } | |
| rl.close(); | |
| const profile: RevpackProfile = { | |
| provider, | |
| }; | |
| if (url) profile.url = url; | |
| if (tokenEnv) profile.tokenEnv = tokenEnv; |
🤖 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 `@src/cli/commands/config.ts` around lines 399 - 450, The profile creation flow
in the config command is leaving `providerUrl` empty for `bitbucket-cloud`,
which breaks validation and remote matching. Update the prompt/assignment logic
around `provider`, `url`, and `RevpackProfile` so that when the selected
provider is `bitbucket-cloud` and the URL input is blank, it defaults to
`https://bitbucket.org` before building the profile. Make sure this happens in
the same path that currently derives `defaultName`, `extraPatternPrompt`, and
the final `profile` object so `runDoctor()` and remote matching continue to
work.
Closes #31
Summary by CodeRabbit
New Features
revpack connectandrevpack doctoras top-level commands.revpack setup --agent <target>for installing setup guidance tailored to specific editors and assistants.Bug Fixes
revpack connect.