Skip to content

REL-000: skeleton for the CLI#708

Open
ari-launchdarkly wants to merge 5 commits into
mainfrom
ari-launchdarkly/REL-0000-cli
Open

REL-000: skeleton for the CLI#708
ari-launchdarkly wants to merge 5 commits into
mainfrom
ari-launchdarkly/REL-0000-cli

Conversation

@ari-launchdarkly
Copy link
Copy Markdown
Contributor

@ari-launchdarkly ari-launchdarkly commented May 12, 2026

Summary

Adds a new ldcli setup command that replaces the existing quickstart as the primary onboarding path. It introduces an interactive Bubble Tea wizard that walks users through project/environment selection, SDK detection, installation, code injection, flag creation, and connectivity verification. It also exposes hidden subcommands (detect, install, init) designed for consumption by external AI agents.

  • New ldcli setup command with an interactive wizard that orchestrates: pick project -> pick environment -> detect language/framework -> install SDK -> create flag -> inject initialization code -> verify SDK connectivity
  • Three hidden subcommands (setup detect, setup install, setup init) that return structured JSON, designed for external AI agents to call programmatically. Flag creation and SDK verification are intentionally not duplicated here since ldcli flags create and ldcli environments get-sdk-active already exist.
  • Detector and Installer interfaces with stub implementations — a @dakotasanchez will provide real detection (language/framework scanning) and installation (package manager execution) logic
  • Initializer with embedded Go templates for 11 SDKs (React, React Native, JS, Swift, Android, Java, Ruby, Go, Python, .NET Server, Node.js Server) that inject SDK initialization code into existing entry point files
  • Docs URL fallback — when no init template exists for an SDK, the initializer returns the appropriate launchdarkly.com/docs/sdk/... URL instead of erroring, covering all 30+ SDKs
  • Verifier that polls GET /api/v2/projects/{project}/environments/{env}/sdk-active with configurable interval and timeout (used internally by the wizard)
  • The old setup (quickstart) command is preserved as ldcli quickstart (hidden) for backward compatibility

Test plan

  • go build ./... passes
  • go test ./cmd/setup/... — 4 tests (init plaintext, init JSON, detect stub error, install stub error)
  • go test ./internal/setup/... — 19 tests (template rendering for all 11 SDKs, file injection new/existing, docs URL fallback for known/unknown SDKs, verifier active/timeout, stub detector/installer errors)
  • Manual: ldcli setup detect returns "not yet implemented" (expected until real Detector is wired)
  • Manual: ldcli setup init --sdk-id node-server --file /tmp/test.js --sdk-key abc --flag-key my-flag injects code
  • Manual: ldcli setup init --sdk-id php-server-sdk --file /tmp/test.php returns docs URL in JSON

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Related issues

Provide links to any issues in this repository or elsewhere relating to this pull request.

Describe the solution you've provided

Provide a clear and concise description of what you expect to happen.

Describe alternatives you've considered

Provide a clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context about the pull request here.


Open in Devin Review

Note

Medium Risk
Introduces a new interactive onboarding flow that makes authenticated API calls and writes to user files, which can affect first-run UX and error handling. Detection/installation are currently stubbed, but the new command wiring and template-based file injection add moderate behavioral surface area.

Overview
Replaces the primary onboarding command with a new guided ldcli setup flow. The root command now registers setup as the main entry point and hides/deprecates the previous wizard under ldcli quickstart.

ldcli setup adds an interactive Bubble Tea wizard that selects a project/environment via API calls, creates a default flag, injects SDK initialization code into a detected entrypoint, and verifies connectivity by polling the sdk-active endpoint.

Adds hidden, agent-friendly subcommands setup detect, setup install, and setup init (plaintext or JSON output), plus a new internal/setup package with Detector/Installer interfaces (stub implementations), an Initializer backed by embedded templates for 11 SDKs with docs-URL fallback, and a Verifier for polling connectivity.

Also adjusts global flag handling so setup (and other non-token commands) bypass the root required --access-token check without disabling subcommand flag validation, and updates CLI usage text accordingly.

Reviewed by Cursor Bugbot for commit 30dbc83. Bugbot is set up for automated code reviews on this repo. Configure here.

devin-ai-integration[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

Comment thread cmd/templates.go

Commands:
{{rpad "setup" 29}} Create your first feature flag using a step-by-step guide
{{rpad "setup" 29}} Set up LaunchDarkly in your project (detect, install, initialize)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We should probably add quickstart in here as well

Comment thread cmd/setup/wizard.go Outdated
verifier := &setup.Verifier{
Client: m.resourcesClient,
Interval: 5 * time.Second,
Timeout: 120 * time.Second,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We should probably pull these out somewhere as const so they're easier to find?

cursor[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 9 additional findings in Devin Review.

Open in Devin Review

Comment thread cmd/setup/wizard.go
Comment on lines +149 to +152
switch msg.String() {
case "ctrl+c", "q":
m.quitting = true
return m, tea.Quit
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot May 13, 2026

Choose a reason for hiding this comment

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

🟡 Pressing 'q' during project/environment list filtering quits the wizard instead of typing into filter

In the wizard's Update method, the q key is unconditionally intercepted and triggers tea.Quit before the key event is delegated to the Bubble Tea list.Model. The list.Model's default behavior when in filter mode is to forward key presses (including q) to its internal text input. By intercepting q at cmd/setup/wizard.go:150-152, the wizard prevents users from ever typing the letter 'q' into the list's search/filter, and instead exits the wizard. This is inconsistent with the existing quickstart wizard (internal/quickstart/help.go:45-46) which only uses ctrl+c for quitting.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread internal/setup/sdk_init_templates/python-server-sdk.tmpl Outdated
cursor[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment thread cmd/root.go
Comment on lines 133 to 139
for _, name := range []string{
"completion",
"config",
"help",
"login",
"setup",
"signup",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Renamed quickstart command lost its access-token exemption, breaking backward compatibility

The old quickstart command had Use: "setup" and was exempted from requiring --access-token via the PersistentPreRun exemption list in cmd/root.go:133-139. The PR renames it to Use: "quickstart" at cmd/root.go:267, but "quickstart" was never added to the exemption list. The "setup" entry now matches only the new setup command.

Cobra's ValidateRequiredFlags checks pflag.Changed, which is only true when the flag is explicitly passed on the command line — not when the value comes from an env var (LD_ACCESS_TOKEN) or config file. So running ldcli quickstart without --access-token on the command line will fail with required flag(s) "access-token" not set, even if the token is available via config/env. Previously this worked because the command matched the "setup" exemption.

Suggested change
for _, name := range []string{
"completion",
"config",
"help",
"login",
"setup",
"signup",
for _, name := range []string{
"completion",
"config",
"help",
"login",
"quickstart",
"setup",
"signup",
} {
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default mode and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 30dbc83. Configure here.

Comment thread cmd/setup/wizard.go
switch msg.String() {
case "ctrl+c", "q":
m.quitting = true
return m, tea.Quit
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wizard "q" key prevents list filter interaction

Medium Severity

The Update function intercepts the "q" keystroke at the top level before delegating to the list.Model sub-components. The charmbracelet/bubbles list.Model supports filter/search mode by default (activated with /). When a user enters filter mode to search for a project or environment name containing the letter "q", the wizard quits instead of passing the keystroke to the list's filter input. Only "ctrl+c" is safe here; the bare "q" binding conflicts with the list's built-in text input.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 30dbc83. Configure here.

Comment thread cmd/setup/setup.go
viper.GetString(cliflags.AccessTokenFlag),
viper.GetString(cliflags.BaseURIFlag),
viper.GetBool(cliflags.AnalyticsOptOut),
).SendCommandRunEvent(cmdAnalytics.CmdRunEventProperties(cmd, "setup", nil))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Transitional notice hidden by immediate alt-screen activation

Low Severity

The PreRun prints a transitional stderr notice about the command change, but RunE immediately starts a Bubble Tea program with tea.WithAltScreen(), which switches the terminal to an alternate screen buffer and hides all prior output. The warning is effectively invisible during the wizard session — by the time the alt screen is exited and the notice becomes visible again, the user has already completed (or abandoned) the new wizard. This undermines the purpose of the transitional warning for users who previously relied on ldcli setup being the quickstart.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: Breaking CLI default changes require transitional stderr warnings

Reviewed by Cursor Bugbot for commit 30dbc83. Configure here.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 11 additional findings in Devin Review.

Open in Devin Review

Comment thread cmd/root.go
"config",
"help",
"login",
"setup",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Setup wizard requires access-token but is exempted from the required-flag check

Adding "setup" to the PersistentPreRun exemption list at cmd/root.go:138 removes the --access-token required-flag validation for both the main ldcli setup wizard and its subcommands. The subcommands (detect, install, init) are purely local operations that don't need an API token, so the exemption is correct for them. However, the main wizard's RunE (the TUI) immediately calls fetchProjects() (cmd/setup/wizard.go:133), which uses viper.GetString(cliflags.AccessTokenFlag) to make authenticated API requests (cmd/setup/wizard.go:340-341). If a user runs ldcli setup without providing an access token via flag, config, or environment variable, the wizard launches the full-screen TUI, shows a spinner, and then fails with a cryptic API authentication error instead of the clear "required flag "access-token" not set" message that Cobra would have provided.

Contrast with other exempted commands

All other commands in the exemption list (completion, config, help, login, signup) genuinely don't need an API token for their core functionality. The setup wizard is the only exempted command whose primary flow requires authenticated API calls.

Prompt for agents
The setup wizard command needs an access token for API calls (fetchProjects, fetchEnvironments, fetchEnvDetails, runCreateFlag, runVerify), but it is exempted from the required-flag check in PersistentPreRun at cmd/root.go:138. The exemption IS needed for the hidden subcommands (detect, install, init) which are local-only operations.

Two possible approaches:

1. Remove setup from the exemption list and instead have each subcommand individually clear the annotation in its own PreRun. This keeps the wizard requiring --access-token while freeing subcommands from needing it.

2. Keep setup in the exemption list but add an early access-token check in the wizard's RunE (in cmd/setup/wizard.go, in the runSetupWizard function) before starting the TUI. For example, check viper.GetString(cliflags.AccessTokenFlag) and return a clear error like 'the setup wizard requires an access token; provide one via --access-token, LD_ACCESS_TOKEN, or ldcli config --set access-token' if empty.

Approach 2 is simpler and provides a better error message than Cobra's default 'required flag not set' message.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

2 participants