Skip to content

feat: add guided setup command - #753

Merged
ffantl-ld merged 8 commits into
setup-ldfrom
ffantl/setup-ld/6-command
Aug 6, 2026
Merged

feat: add guided setup command#753
ffantl-ld merged 8 commits into
setup-ldfrom
ffantl/setup-ld/6-command

Conversation

@ffantl-ld

@ffantl-ld ffantl-ld commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Describe the solution you've provided

Final layer: the user-facing setup command. Adds the cmd/setup package (detect, install, init subcommands and the interactive wizard) over the internal/setup library, wires it into the root command, and deprecates quickstart in favor of setup.

The ldcli setup wizard walks a user through detecting their project, installing the SDK, creating a first flag, injecting init code, and verifying an evaluation.

Related issues

Top of the setup-ld feature stack. Stacked PR — base is ffantl/setup-ld/4-verifier.

Note: this branch's cmd/root.go also carries the PersistentPreRun change from #748; that diff drops out once #748 merges to main and this stack is rebased.

Requirements

  • I have added test coverage for new or changed functionality

Note

Medium Risk
Large new surface that modifies local files, runs package installs, and creates flags via the API; auth-exempt flag handling changed globally for several commands, though coverage is extensive.

Overview
Adds ldcli setup as the primary guided onboarding path: an interactive wizard (project/environment selection, local detection, SDK install, flag creation, init injection, optional live verify) plus hidden setup detect, install, and init for scripting.

ldcli quickstart remains registered but is hidden and deprecated in favor of setup. Root wiring injects setup.Detector / Installer defaults and extends APIClients accordingly.

Auth handling for commands that do not need --access-token is reworked: setup is added to the exempt set, and clearAccessTokenRequirement drops the required annotation on --access-token instead of DisableFlagParsing, so subcommand flags still validate.

Supporting API/library changes: paginated List on projects/environments; flag Create accepts WithClientSideAvailability for JS/React SDKs; init injection skips files that already contain SDK setup (AlreadyInitialized); new internal/setup.Service orchestrates the wizard steps.

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

@ffantl-ld
ffantl-ld force-pushed the ffantl/setup-ld/6-command branch from 56fef68 to 3680877 Compare July 23, 2026 15:16
@ffantl-ld
ffantl-ld marked this pull request as ready for review July 27, 2026 17:23
@ffantl-ld
ffantl-ld requested review from Vadman97 and erangeles July 27, 2026 17:23
Comment thread cmd/root.go
Comment thread cmd/setup/wizard.go Outdated
Comment thread cmd/setup/wizard.go Outdated
@ffantl-ld

ffantl-ld commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Review resolution map — the 8 inline findings from the original review on the superseded #744 were migrated and resolved as follows (that PR was closed when this work was split into the #748–753 stack, so the threads were stranded there):

Finding File Disposition
Flag-skip affects 6 commands, not just setup cmd/root.go Verified — go test ./cmd/... green, completion/config/whoami smoked clean (#748)
Install-failure reason swallowed cmd/setup/wizard.go Fixed97c6a7d (this PR): InstallResult.FailureReason rendered on failure screen
Hardcoded app.launchdarkly.com cmd/setup/wizard.go Fixed97c6a7d (this PR): derives host from --base-uri
No pagination on project/env lists cmd/setup/wizard.go Deferred — REL-15105
node template assumes CommonJS (ESM breaks) internal/setup/sdk_init_templates/node-server.tmpl Deferred — REL-15106
parseJSONError string-coupling cmd/setup/wizard.go Deferred — REL-15107
Python pkg-manager arg inconsistency internal/setup/installer.go Won't fix — low-risk, exec not shell, author-intended
Guessed Android entry-point path internal/setup/detector.go Won't fix — guidance-only, cosmetic

via LD Research 🤖

@ffantl-ld
ffantl-ld requested review from a team and removed request for Vadman97 July 28, 2026 16:38
@ffantl-ld
ffantl-ld force-pushed the ffantl/setup-ld/6-command branch from 97c6a7d to 73cc570 Compare July 31, 2026 17:48
@ffantl-ld
ffantl-ld force-pushed the ffantl/setup-ld/6-command branch from 73cc570 to fa402b8 Compare July 31, 2026 18:25
@ffantl-ld
ffantl-ld force-pushed the ffantl/setup-ld/6-command branch from fa402b8 to 4799b3e Compare August 3, 2026 18:56
Comment thread cmd/setup/view.go
Comment thread cmd/setup/update.go
Comment thread cmd/setup/update.go
@ffantl-ld
ffantl-ld force-pushed the ffantl/setup-ld/6-command branch from 4799b3e to eeb4ae7 Compare August 3, 2026 19:48
@ffantl-ld
ffantl-ld force-pushed the ffantl/setup-ld/6-command branch from eeb4ae7 to cdc28f6 Compare August 5, 2026 18:06
Comment thread internal/setup/service.go
Comment thread cmd/setup/install.go
Comment thread cmd/setup/model.go Outdated
Base automatically changed from ffantl/setup-ld/4-verifier to setup-ld August 5, 2026 19:30
ffantl-ld and others added 2 commits August 5, 2026 15:30
ldcli setup walks a project through installing a LaunchDarkly SDK: detect the
language and package manager, pick a project and environment, install the SDK
with the project's own tool, create a flag, write or show initialization code,
then poll until the SDK connects.

Orchestration lives in internal/setup.Service so the wizard UI and the
detect/install/init subcommands share one path. The wizard is split into model,
update, view, and commands rather than one file.

Environments gains List so the wizard can offer a choice of environments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* REL-15243: add a copy key for wizard code blocks

Code blocks are drawn with a left gutter bar, so selecting one by hand copies the
gutter characters and the padding lipgloss squares the block off with. The wizard
also owns the alternate screen, so the snippet is not in scrollback once it exits.

Pressing c writes the raw content to the system clipboard with OSC 52, preferring
the snippet over the install command when a screen shows both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* REL-15243: copy through the OS clipboard before the terminal

OSC 52 alone left the key unreliable: terminals are not required to implement it,
Apple Terminal does not, and support cannot be queried, so the confirmation claimed
a copy that may never have happened. The OS clipboard works in any terminal and
returns an error, so try it first and keep OSC 52 for when it fails — which is the
SSH case, where the OS clipboard belongs to the wrong machine.

Word the two outcomes apart, since only the first can be confirmed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* REL-15243: skip the OS clipboard over SSH

A remote host can have a working clipboard, so writing to it succeeds while putting
the snippet on a machine the user is not pasting into — and the confirmation then
claimed the copy was done. Detect an SSH session from the environment sshd sets and
go straight to the terminal, which is the end the user is actually at.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ffantl-ld
ffantl-ld force-pushed the ffantl/setup-ld/6-command branch from 6cc0ce8 to f73e51c Compare August 5, 2026 19:31
The plaintext `setup install` output only reported `Success: false`, and the
wizard's manual-install screen printed an empty code block whenever the
installer declined to run without erroring, so neither told the user what to
do next.

Print the installer's FailureReason, explain SDKs that have no automated
command at all, and lead the wizard screen with the reason — offering a
command only when one exists, since the reason already carries the right
command when the installer declined up front.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four review findings in the wizard:

Projects and environments were read from the first API page only, hiding
everything past the first 20 from accounts and projects larger than that.
Both list calls now take a limit and offset and page until a short page.

An empty list was indistinguishable from a list still loading, so a token
scoped to no projects parked on the spinner with no way out. Fetch completion
is now tracked separately from item count, with an empty state for each.

Terminal resizes never reached the lists, which were sized once at
construction. WindowSizeMsg now pushes the new size into every list that has
been built; SetSize panics on a zero-value list.Model, so the guards matter.
List height is clamped so it cannot go negative before the first size message.

OSC 52 was written to stdout, which Bubble Tea owns for frame rendering while
the wizard runs, so the sequence could land mid-frame on exactly the SSH path
that depends on it. It now goes to stderr, or to the controlling terminal when
stderr is not one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread cmd/setup/update.go
Selecting a second project left the first project's environments in the model
while the new fetch was in flight, so the list rendered immediately and Enter
committed an environment key the new project does not have — the wizard then
ran against a mismatched project and environment. A previously empty result
had the milder version of the same fault, showing the empty state and making a
non-empty project look empty.

Back from the SDK step deliberately keeps the list, since that transition does
not re-fetch and clearing it would strand the user on a spinner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread cmd/setup/update.go
ffantl-ld and others added 3 commits August 6, 2026 12:51
Nothing cancels an in-flight fetch, so its response was applied whatever the
model had done meanwhile. Pressing enter on an environment and then going back
let the arriving keys pull the wizard into SDK selection with no environment
selected, which sent verification to an empty environment path; a response for
a project the user had already left rebuilt the list under the new project,
letting enter commit an environment key that project does not have.

Both messages now name the project and environment they were fetched for, and
are dropped unless that still matches the selection and the wizard is on a step
that is waiting for them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Injection appended unconditionally, so running setup a second time wrote a
second copy of the init code. In Node that redeclares const bindings and the
app stops starting with a SyntaxError, while both runs reported success. The
second run is an ordinary path: install is skipped as already installed and a
flag conflict counts as success, so nothing else stops it.

The entry file is now checked for the template's own import lines before
anything is written, and the wizard says the file was left as it is rather than
claiming to have injected code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The API leaves usingEnvironmentId false unless a create request asks otherwise,
so the flag the wizard created was invisible to the js and react SDKs it had
just installed: variation() returned the fallback while the final screen said
the flag was ready. Flag creation now requests client-side availability when the
chosen SDK authenticates with the client-side ID, which is read from the SDK's
own init template so a new template cannot disagree with a separate list.

Create takes options, so the deprecated quickstart path keeps the API defaults.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 effort 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 4dc11df. Configure here.

Comment thread internal/setup/service.go
Comment thread internal/setup/initializer.go
@ffantl-ld
ffantl-ld merged commit 5c14b6c into setup-ld Aug 6, 2026
4 of 6 checks passed
@ffantl-ld
ffantl-ld deleted the ffantl/setup-ld/6-command branch August 6, 2026 17:59
ffantl-ld added a commit that referenced this pull request Aug 7, 2026
* feat: add guided setup command

ldcli setup walks a project through installing a LaunchDarkly SDK: detect the
language and package manager, pick a project and environment, install the SDK
with the project's own tool, create a flag, write or show initialization code,
then poll until the SDK connects.

Orchestration lives in internal/setup.Service so the wizard UI and the
detect/install/init subcommands share one path. The wizard is split into model,
update, view, and commands rather than one file.

Environments gains List so the wizard can offer a choice of environments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(setup): copy wizard code blocks with c (#771)

* REL-15243: add a copy key for wizard code blocks

Code blocks are drawn with a left gutter bar, so selecting one by hand copies the
gutter characters and the padding lipgloss squares the block off with. The wizard
also owns the alternate screen, so the snippet is not in scrollback once it exits.

Pressing c writes the raw content to the system clipboard with OSC 52, preferring
the snippet over the install command when a screen shows both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* REL-15243: copy through the OS clipboard before the terminal

OSC 52 alone left the key unreliable: terminals are not required to implement it,
Apple Terminal does not, and support cannot be queried, so the confirmation claimed
a copy that may never have happened. The OS clipboard works in any terminal and
returns an error, so try it first and keep OSC 52 for when it fails — which is the
SSH case, where the OS clipboard belongs to the wrong machine.

Word the two outcomes apart, since only the first can be confirmed.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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