[REL-14909] Revive CLI Quickstart Wizard — integrate #708 + #709#744
[REL-14909] Revive CLI Quickstart Wizard — integrate #708 + #709#744ffantl-ld wants to merge 33 commits into
Conversation
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
Co-authored-by: ari-launchdarkly <asalem@launchdarkly.com>
Address the six findings from the independent test pass on the revived CLI onboarding wizard: - init: no longer writes non-compiling code for scoped/framework SDKs. Only script languages (node/python/ruby) are appended in place; Go, Java, C#, Swift, Android, React, and React Native now return the rendered snippet + docs URL as guidance with success=false instead of corrupting the entry file and reporting success. Drops the Go AST import-merge path (dead once Go is guidance-only). - react: covered by the guidance path above (asyncWithLDProvider must be wired into the component tree, not appended). - detect: add a Ruby detector (Gemfile / Gemfile.lock / config.ru / *.gemspec). - install: add --dry-run to print the command without executing; unknown SDK IDs now error instead of silently returning success=false, while genuine manual-install SDKs (Java/Android/Swift) still return success=false. - gofmt: format previously-misindented files. - detect: clarify with a comment that Next.js intentionally maps to the Node server SDK (server-side eval for SSR / API routes). Tests updated to assert guidance behavior for scoped/framework SDKs and to cover Ruby detection, dry-run, and the unknown-SDK error. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Running `ldcli setup` without an access token launched the TUI and then
died on the first API call with a raw 401 JSON dump ("Invalid account ID
header") and no guidance — the exact first-run case a new user hits.
Check for an access token before launching the wizard and return a clear
message pointing at `ldcli login` (or --access-token / LD_ACCESS_TOKEN)
instead. Adds a test covering the no-token path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reword the pre-flight auth message to a conversational nudge that points at `ldcli login` as the next step. On the SDK selection screen, when detection succeeded, replace the generic "Select your SDK:" title with "We've detected <SDK> — press Enter to use it, or choose a different SDK below:" so it's clear the top entry is the auto-detected one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The quickstart-transition notice printed flush against command output, reading as part of it. Render it faint and follow it with a blank line so it's visually distinct from the wizard's output and messages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The detected SDK now appears in its own "We identified this as your SDK" panel above a separate list of the remaining SDKs. Arrow keys move focus between the two areas (the focused one is highlighted); Enter on the panel accepts the detected SDK, Enter in the list picks another. When detection fails, only the list is shown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The success, manual-setup, and timed-out screens are terminal states but gave no exit hint. Append a faint "Press q to quit." to each. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The detected-SDK panel gave no cursor, so the list's highlight on the first other SDK read as the active choice. Add a purple pointer to the detected SDK while its panel is focused. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Give the detected-SDK panel and the other-SDKs list one shared width so they line up, and render the list's selected row as a normal row while focus is on the panel — so only the focused area shows an active selection instead of both the panel and the first list row. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Before running the package manager, check whether the SDK's package is already present in the project manifest (package.json, go.mod, requirements.txt, Gemfile, *.csproj). If so, return AlreadyInstalled and skip the install instead of re-running it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After the SDK is chosen, show a plan of what setup will do — install (or "already installed, will skip"), create a flag, add init code (or show a snippet), and verify — and wait for Enter before taking any action. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the "Hey —" opener and put the call to action on its own line after a blank line, so the message reads cleanly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the per-screen color literals (6/205/240) scattered across the wizard with a small shared palette and style set in styles.go, aligned with ldcli's quickstart TUI (selected 170, border 62). Screens now share titleStyle/headerStyle/selectedStyle/mutedStyle and a box() helper instead of re-declaring them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The list rendered its title with the default colored title bar while the detected-SDK panel header was plain bold, so the two headings looked inconsistent. Style the list title with the same header style. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Left arrow steps back through the flow (plan -> SDK -> environment -> project) so the SDK, environment, or project can be changed. Esc quits. Both are suppressed while a list filter is active so they don't hijack filter editing. Screens show the "← back · esc quit" hints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Detection ran when the SDK screen was reached and re-ran whenever the user navigated back and forward. Kick it off in Init instead, cache the result, and build the SDK screen from the cache — so moving through the flow is instant and detection never runs twice. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SDK screen showed two key hints — the list's built-in help footer and a separate line below. Turn off the list's built-in help and render one hint inside the other-SDKs box (↑/↓ move · enter select · ← back · esc quit). Also accept vim `h` as a back key alongside the left arrow (j/k already handle down/up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rapping - A failed auto-install (e.g. Ruby gem perms, no network) no longer dead-ends the wizard: it continues and the final screen shows the command to run by hand, and skips the live verify. - SDKs with no automated install (Java/Android/Swift) are marked "(manual install)" in the SDK list and detected panel. - The SDK screen links to the full SDK catalog so an unlisted language doesn't read as unsupported. - Boxes clamp to the terminal width and prose reflows, so narrow terminals don't overflow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Manual-setup and failed-install screens printed the copy-me snippet and install command as plain text, indistinguishable from surrounding prose. Render them through a code style (left gutter bar, distinct foreground) so the user can tell what to copy versus what to read. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Superseded by a progressive split of this work. Landing as small, reviewable PRs onto the
|
ffantl-ld
left a comment
There was a problem hiding this comment.
Review of the revived setup wizard. Structure and test coverage are solid. Comments below, highest-value first — the root.go one touches shared behavior outside setup/ and is worth verifying before un-drafting.
via LD Research 🤖
| // that don't need an API token. We clear the annotation on the | ||
| // specific flag instead of setting DisableFlagParsing, which would | ||
| // also suppress validation for the subcommand's own required flags. | ||
| for _, name := range []string{ |
There was a problem hiding this comment.
Shared behavior change for 6 commands, not just setup. The old code set DisableFlagParsing = true for completion/config/help/login/signup/whoami; this replaces that with only clearing the --access-token required annotation. Cleaner intent, but not equivalent: any of those commands that relied on flag parsing being disabled — or had other required flags previously skipped — now behave differently. completion in particular passes args through.
Please run go test ./cmd/... on the rebased tree and smoke ldcli completion bash, ldcli config, ldcli whoami before un-drafting. This is the only change reaching outside setup/.
via LD Research 🤖
|
|
||
| // Commands that perform async work | ||
|
|
||
| func (m wizardModel) fetchProjects() tea.Cmd { |
There was a problem hiding this comment.
No pagination. /api/v2/projects defaults to 20 items. A user with >20 projects silently can't select the rest. Same for fetchEnvironments. Add ?limit= + follow _links.next, or at minimum request a high limit. Filing a REL follow-up unless you want it in this PR.
via LD Research 🤖
| @@ -0,0 +1,15 @@ | |||
| const LaunchDarkly = require('@launchdarkly/node-server-sdk'); | |||
There was a problem hiding this comment.
Assumes CommonJS. node-server is append-safe, so this require(...) is written directly into the entry file. A project with "type": "module" in package.json throws at runtime. The detector doesn't read module type. Either detect ESM and emit import, or degrade node to guidance-only for ESM projects.
via LD Research 🤖
| Message string `json:"message"` | ||
| } | ||
|
|
||
| func parseJSONError(err error) (*jsonError, error) { |
There was a problem hiding this comment.
Fragile coupling. parseJSONError assumes err.Error() is raw JSON. If flagsClient.Create ever wraps the error (e.g. fmt.Errorf), conflict detection breaks and the wizard hard-errors instead of reusing the existing my-new-flag. Prefer a typed/sentinel error from the flags client.
via LD Research 🤖
| if err != nil { | ||
| // Don't dead-end on a failed auto-install (e.g. Ruby gem perms, no | ||
| // network): continue and surface the command to run by hand. | ||
| args, _ := setup.InstallArgs(m.detectResult.SDKID, m.detectResult.PackageManager) |
There was a problem hiding this comment.
Install error is swallowed. The real err (network, gem perms, etc.) is discarded here; the manual-install screen shows only the command. The user can't tell why the auto-install failed. Carry the error message onto the failure screen.
via LD Research 🤖
| return titleStyle.Render("Setup complete!") + "\n\n" + | ||
| fmt.Sprintf("Your %s SDK is connected to LaunchDarkly.\n", m.detectResult.SDKID) + | ||
| fmt.Sprintf("Flag %q is ready to use.\n\n", m.flagKey) + | ||
| fmt.Sprintf("You can now toggle your flag at https://app.launchdarkly.com/projects/%s/flags/%s/targeting?env=%s\n", m.selectedProject, m.flagKey, m.selectedEnv) + |
There was a problem hiding this comment.
Hardcoded app host. https://app.launchdarkly.com ignores baseURI — wrong link on federal / custom instances. Derive from the configured base URI.
via LD Research 🤖
| pkg = "@launchdarkly/js-client-sdk" | ||
| return nodeInstallCmd(resolveNodePM(packageManager), pkg), pkg | ||
| case "python-server-sdk": | ||
| pm := packageManager |
There was a problem hiding this comment.
Minor / intentional-per-test: the Python path uses --package-manager verbatim as args[0] while the Node path normalizes it. Not shell-injectable (exec, not sh) and it's the user's own machine + flag, so low risk — just flagging the inconsistency.
via LD Research 🤖
| Language: "Java", | ||
| PackageManager: "gradle", | ||
| SDKID: "android-client-sdk", | ||
| EntryPoint: filepath.Join(dir, "app/src/main/java/MainActivity.java"), |
There was a problem hiding this comment.
Cosmetic: app/src/main/java/MainActivity.java is a guessed path — Android sources normally sit under package dirs. Android is guidance-only (not injected), so this only affects the displayed hint.
via LD Research 🤖
Summary
Integration branch for REL-14909 — reviving the deterministic CLI Quickstart Wizard (
ldcli setup). Stacks the two moonshot PRs on top of currentmain:ldcli setupskeleton: interactive Bubble Tea wizard (pick project → env → detect → install → create flag → inject init code → verify) + hidden agent-facing JSON subcommands (detect/install/init) +Initializertemplates for 11 SDKs with docs-URL fallback +Verifier. (Was closed; approved + green.)FileDetector(Node/React/Next, Go, Python, Java) andPackageInstaller(shells out to npm/yarn/pnpm/pip/go/gem/dotnet; manual-install SDKs returnSuccess:false) filling in REL-000: skeleton for the CLI #708's stubs.Rebased #709's branch (
dsanchez/REL-13574/cli, itself based on #708'sari-launchdarkly/REL-0000-cli) onto currentmain— 15 commits, clean rebase,go build ./...passes.Status — DRAFT, needs validation
Opened as a draft to get an end-to-end test pass. Known open items:
sdk-active).go test ./...on the rebased tree.Intent
Deterministic, bounded, inspectable CLI install flow (Plan item #3 / H2 of the Agentic Onboarding plan) — same automation as the AI-install path, offered to the ~49% who prefer manual install and the segment an open-ended agent can't serve. The hidden JSON subcommands mean one engine backs both this wizard and external AI agents.
Ref: REL-14909 · supersedes moonshot epic REL-13574.