Conversation
* chore(setup): add SDK/project detection library Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): report whether the detected entry point exists DetectResult carries EntryPointExists so callers can tell an entry file the detector found from one it merely suggests, and never write initialization code into a path the project does not load. PackageManager names the tool that manages the project's dependencies: bundle rather than gem when a Gemfile is present, and poetry, uv or pipenv rather than always pip. Locate MainActivity and Main under their real package directory rather than assuming an unqualified class name, derive the Android source root from whichever manifest matched, find the Swift entry point where SwiftPM and Xcode nest it, look for src/main.tsx where Vite mounts a React app, and recognise bun.lockb. Rename the Android SDK ID to android for consistency with the other IDs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): keep the Next.js SDK key out of the browser bundle Next.js detection targeted whichever page module happened to exist, and node-server is append-safe, so setup wrote server SDK init — including the SDK key — into app/page.tsx or pages/index.tsx. A page module may carry 'use client' or be imported by something that does, which bundles it for the browser, and nothing in the detector can tell which. Only instrumentation.ts, Next's server-startup hook, is guaranteed to stay server-side, so suggest creating it rather than picking a page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(setup): cite the sources for the entry-point candidates Each candidate list encodes a claim about where a toolchain puts its entry file. Link the documentation that claim rests on so it can be rechecked when the frameworks move. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): detect backend manifests before package.json A root package.json is often only build tooling — Rails with jsbundling, Django with Tailwind, a Go binary published to npm — so preferring Node whenever one parsed meant those projects were handed the Node SDK. This repo hit it too. Confine the Sources/ search to single-target Swift packages. Across several targets there is no way to tell an executable's entry file from a library's, so an arbitrary hit was reported as found. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(setup): assert the whole result per project shape The existing tests check one or two fields each, so a field detection stops populating passes as long as the SDK id stays right. Compare the full DetectResult across the project layouts real toolchains produce. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): find the src/main entry a Node app bootstraps from NestJS and similar apps start from src/main.ts, which the candidate list skipped, so detection suggested a nonexistent index.js. node-server appends to the entry file, so setup created that index.js and left the real entry point without the SDK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(setup): add SDK/project detection library Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): report whether the detected entry point exists DetectResult carries EntryPointExists so callers can tell an entry file the detector found from one it merely suggests, and never write initialization code into a path the project does not load. PackageManager names the tool that manages the project's dependencies: bundle rather than gem when a Gemfile is present, and poetry, uv or pipenv rather than always pip. Locate MainActivity and Main under their real package directory rather than assuming an unqualified class name, derive the Android source root from whichever manifest matched, find the Swift entry point where SwiftPM and Xcode nest it, look for src/main.tsx where Vite mounts a React app, and recognise bun.lockb. Rename the Android SDK ID to android for consistency with the other IDs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): keep the Next.js SDK key out of the browser bundle Next.js detection targeted whichever page module happened to exist, and node-server is append-safe, so setup wrote server SDK init — including the SDK key — into app/page.tsx or pages/index.tsx. A page module may carry 'use client' or be imported by something that does, which bundles it for the browser, and nothing in the detector can tell which. Only instrumentation.ts, Next's server-startup hook, is guaranteed to stay server-side, so suggest creating it rather than picking a page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(setup): cite the sources for the entry-point candidates Each candidate list encodes a claim about where a toolchain puts its entry file. Link the documentation that claim rests on so it can be rechecked when the frameworks move. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): detect backend manifests before package.json A root package.json is often only build tooling — Rails with jsbundling, Django with Tailwind, a Go binary published to npm — so preferring Node whenever one parsed meant those projects were handed the Node SDK. This repo hit it too. Confine the Sources/ search to single-target Swift packages. Across several targets there is no way to tell an executable's entry file from a library's, so an arbitrary hit was reported as found. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(setup): assert the whole result per project shape The existing tests check one or two fields each, so a field detection stops populating passes as long as the SDK id stays right. Compare the full DetectResult across the project layouts real toolchains produce. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): find the src/main entry a Node app bootstraps from NestJS and similar apps start from src/main.ts, which the candidate list skipped, so detection suggested a nonexistent index.js. node-server appends to the entry file, so setup created that index.js and left the real entry point without the SDK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(setup): add SDK installer library Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): install with the project's own package manager `gem install` left the Gemfile untouched, so the SDK stayed unavailable under bundler and IsInstalled kept returning false. Use `bundle add` when the project is Bundler-managed, and poetry, uv or pipenv when one of those manages the Python dependencies. Unrecognised package managers fall back to pip rather than being run as a command, since the value reaches InstallArgs from the detector. InstallArgs added launchdarkly-react-native-client-sdk, which npm marks deprecated in favour of @launchdarkly/react-native-client-sdk. The unscoped launchdarkly-js-client-sdk is the v3 package whose initialize API the init template uses; the scoped one is v4 and exposes createClient. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): match whole packages and target the right .NET project IsInstalled tested for its package name as a substring, so @launchdarkly/node-server-sdk-redis, launchdarkly-server-sdk-otel, and LaunchDarkly.ServerSdk.Telemetry each counted as the SDK itself and the real install was skipped. Require a non-name character on both sides, which every manifest format supplies. Detection accepts a solution with no project file beside it, but install ran a bare `dotnet add package`, which needs the working directory to hold exactly one project. Resolve the project the solution refers to and pass --project; with none or several, stop and say so rather than adding the SDK to an arbitrary assembly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(setup): add SDK/project detection library Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): report whether the detected entry point exists DetectResult carries EntryPointExists so callers can tell an entry file the detector found from one it merely suggests, and never write initialization code into a path the project does not load. PackageManager names the tool that manages the project's dependencies: bundle rather than gem when a Gemfile is present, and poetry, uv or pipenv rather than always pip. Locate MainActivity and Main under their real package directory rather than assuming an unqualified class name, derive the Android source root from whichever manifest matched, find the Swift entry point where SwiftPM and Xcode nest it, look for src/main.tsx where Vite mounts a React app, and recognise bun.lockb. Rename the Android SDK ID to android for consistency with the other IDs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): keep the Next.js SDK key out of the browser bundle Next.js detection targeted whichever page module happened to exist, and node-server is append-safe, so setup wrote server SDK init — including the SDK key — into app/page.tsx or pages/index.tsx. A page module may carry 'use client' or be imported by something that does, which bundles it for the browser, and nothing in the detector can tell which. Only instrumentation.ts, Next's server-startup hook, is guaranteed to stay server-side, so suggest creating it rather than picking a page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(setup): cite the sources for the entry-point candidates Each candidate list encodes a claim about where a toolchain puts its entry file. Link the documentation that claim rests on so it can be rechecked when the frameworks move. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): detect backend manifests before package.json A root package.json is often only build tooling — Rails with jsbundling, Django with Tailwind, a Go binary published to npm — so preferring Node whenever one parsed meant those projects were handed the Node SDK. This repo hit it too. Confine the Sources/ search to single-target Swift packages. Across several targets there is no way to tell an executable's entry file from a library's, so an arbitrary hit was reported as found. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(setup): assert the whole result per project shape The existing tests check one or two fields each, so a field detection stops populating passes as long as the SDK id stays right. Compare the full DetectResult across the project layouts real toolchains produce. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): find the src/main entry a Node app bootstraps from NestJS and similar apps start from src/main.ts, which the candidate list skipped, so detection suggested a nonexistent index.js. node-server appends to the entry file, so setup created that index.js and left the real entry point without the SDK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(setup): add SDK installer library Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): install with the project's own package manager `gem install` left the Gemfile untouched, so the SDK stayed unavailable under bundler and IsInstalled kept returning false. Use `bundle add` when the project is Bundler-managed, and poetry, uv or pipenv when one of those manages the Python dependencies. Unrecognised package managers fall back to pip rather than being run as a command, since the value reaches InstallArgs from the detector. InstallArgs added launchdarkly-react-native-client-sdk, which npm marks deprecated in favour of @launchdarkly/react-native-client-sdk. The unscoped launchdarkly-js-client-sdk is the v3 package whose initialize API the init template uses; the scoped one is v4 and exposes createClient. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): match whole packages and target the right .NET project IsInstalled tested for its package name as a substring, so @launchdarkly/node-server-sdk-redis, launchdarkly-server-sdk-otel, and LaunchDarkly.ServerSdk.Telemetry each counted as the SDK itself and the real install was skipped. Require a non-name character on both sides, which every manifest format supplies. Detection accepts a solution with no project file beside it, but install ran a bare `dotnet add package`, which needs the working directory to hold exactly one project. Resolve the project the solution refers to and pass --project; with none or several, stop and say so rather than adding the SDK to an arbitrary assembly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(setup): add SDK init/injection library and templates Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): correct the React Native init snippet react-native.tmpl bound the default export of @launchdarkly/react-native-client-sdk, which has no default export, so the snippet could not compile. The client is the named export ReactNativeLDClient. Register the android template under the SDK ID the detector reports, keeping android-client-sdk as an alias. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): pass the required auto-env argument in mobile snippets The Swift and Android snippets did not compile. LDConfig's only public initializer takes autoEnvAttributes, and LDConfig.Builder's only constructor takes AutoEnvAttributes, so neither config could be built as written. These SDKs return a snippet for the user to paste rather than writing a file, so the snippet is the entire deliverable. AutoEnvAttributes is nested in LDConfig.Builder, which the package wildcard import does not cover, so import it explicitly. Add DefaultEntryPoint, naming the file to create for the SDKs that write one when detection found no entry point for them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): build the Swift context without try LDContextBuilder.build returns a Result, and the snippet unwrapped it with `try ...get()`. try only compiles inside a throwing function, and the places this snippet gets pasted — application(_:didFinishLaunchingWithOptions:) and similar startup hooks — do not throw. Match on the Result instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): keep the shebang first when injecting imports InjectIntoFile prepended the import section at byte 0, which displaced a leading shebang and an encoding cookie. Django's manage.py is one of the Python entry points detection targets, so `./manage.py` stopped being executable after setup wrote to it, and Python stopped honoring a coding declaration pushed past line 2. Peel the prologue off first and insert imports after it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): inject node imports in the entry file's module syntax The node-server template loaded the SDK with require, but detection targets TypeScript and ESM entry points such as Next.js instrumentation.ts and NestJS src/main.ts, where require is not defined at runtime. Setup wrote that code and reported success, so the failure only showed up when the app started. Pick the CommonJS or ESM template from the entry extension, falling back to the nearest package.json "type" for a plain .js file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): keep leading directives above injected imports Injecting imports after only the shebang and encoding cookie broke the other constructs a file has to open with. A Python __future__ import below the injected imports is a SyntaxError, a module docstring pushed down is demoted to a plain expression, and Ruby magic comments and a CommonJS 'use strict' are silently ignored once code precedes them. All of it was written with Success: true. Peel the shebang, the leading comment block, and the language's own leading constructs, then insert imports after them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): treat block comments as part of the leading header A /* */ license or JSDoc header stopped the prologue scan, so imports landed above it and above the 'use strict' that followed, silently dropping the file out of strict mode. A directive with a trailing same-line comment was missed for the same reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(setup): add setup flag verifier Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): narrow the verify check to the configured SDK sdk-active answers for any SDK that initialized in the environment in the past seven days, so verification passed on unrelated traffic and a project already using LaunchDarkly reported success without the new SDK ever connecting. Filter on sdk_name, mapping the setup SDK id to the name the SDK reports itself as. An unknown id sends no filter rather than one that can never match, and VerifyResult records which name was used so an unnarrowed check cannot present itself as narrowed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* 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>
Contributor
Author
|
I'm following up on a couple of bugs found on this branch. Will post the ticket and PRs as soon as they're ready. |
Arrow keys arrive as the escape sequence ESC [ A. bubbletea v1.3.6 reports a bare esc whenever a read returns only the first byte, so scrolling a list could drop the user out of the wizard at random. Quit is now q or ctrl+c only, and the on-screen hints match. Leaving esc unbound also returns it to the list for clearing an active filter, which fixes the stranded filter state that silently disabled q on the SDK screen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 17, 2026
bubbles binds a list's quit key to both q and esc and returns tea.Quit when either matches, so dropping our own esc binding was not enough: the key fell through to whichever list was on screen and ended the session from there. Each list's quit key is now q alone. esc still clears an active filter, which the list matches ahead of quitting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # cmd/root.go # go.mod
Availability was set only when an SDK's init template interpolates the client-side ID, so React Native, Android and Swift — which take the mobile key — created flags with mobile availability off. On a project whose defaults are off, those SDKs evaluate the fallback forever while setup reports success. Both settings are now always on: the SDK a project starts with does not say which credential it will end up using. A flag that already exists is still left exactly as it is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ffantl-ld
marked this pull request as draft
August 19, 2026 18:16
ffantl-ld
marked this pull request as ready for review
August 20, 2026 15:48
The workflow asked for whatever Go is newest, so the 1.27.0 release broke every pull request at once: golangci-lint reads type data written by the compiler, and the pinned linter cannot read a format newer than the Go it was released for. The dependency scan already reads go.mod for this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(setup): resolve pip and python from PATH before installing Recent macOS and Homebrew installs ship python3/pip3 with no bare python or pip, so the hardcoded `pip install` failed outright on a common developer box. Probe pip3 then pip, falling back to `<interpreter> -m pip` for interpreters installed without a pip shim. Every package manager is now checked for existence before being run, so a missing tool reports what to install instead of an exec "not found" error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): verify pip is importable before using the -m pip form Debian and Ubuntu package pip separately from the interpreter, so a present python3 does not mean `python3 -m pip` can run. The pre-flight check only looked at the executable, so those boxes got "No module named pip" instead of guidance. Stub PATH in the two install tests that reached for a real npm, which the pre-flight check made environment-dependent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): never substitute a Python interpreter for a missing pip Using `python3 -m pip`, or bootstrapping pip with ensurepip, would install tooling onto the user's machine. Setup only ever uses a pip that is already there; when none is found it warns and runs nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): install Python packages into the project virtualenv pip refuses to write into an OS-managed Python, which Homebrew and most current distributions now mark, so a project with no active virtualenv could not complete setup at all. A virtualenv's pip is used when the project or the environment has one, and the refusal is explained rather than passed through as pip's raw error. InstallArgs takes the project directory so the previewed command is the one that actually runs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(setup): neutralise the ambient virtualenv for the package pipInstallCmd prefers VIRTUAL_ENV over anything on PATH, so running the suite inside an activated environment resolved install commands to that environment's pip and failed assertions about pip and pip3. Tests that want an active virtualenv opt in with stubVirtualEnv. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): resolve the virtualenv pip to an absolute path The install runs with its working directory set to the project, and a relative executable path is resolved after that change, so a relative project directory was applied twice: "app/.venv/bin/pip" run in "app" was looked for at "app/app/.venv/bin/pip" and the install failed even though the venv was found. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): report a virtualenv that has no pip `uv venv` creates a virtualenv without pip, which was read as no virtualenv at all: setup then reached for a pip on PATH, installing outside the project the user set up — or being refused by PEP 668 and advising them to create the virtualenv already sitting there. That case is now named, with how to install into it. A PEP 668 refusal no longer carries the command that refused. The done screen offers a non-empty command as "install it yourself with", which contradicted the reason telling them not to run it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): name the project virtualenv in the previewed pip command The pip-less virtualenv guard sat only in Install, so the plan screen, --dry-run and the picker still previewed a pip from PATH that Install would refuse to run — and a command shown there is one a reader may run by hand, installing outside the project or hitting a raw PEP 668 error. The command now names the virtualenv's own pip whenever the project has one, present or not, so every surface and the runner agree. Install still explains why an unseeded virtualenv cannot be installed into. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): use the platform's virtualenv layout and flag an unrecorded dependency A virtualenv with no pip was always named bin/pip, so on Windows the plan and the previewed command pointed at a layout that environment never uses. The platform's own layout is named first, and both are still considered. A bare pip install also leaves the project's manifest untouched, so a fresh checkout and CI miss the SDK. poetry, uv and pipenv record it themselves and Ruby has `bundle add`; pip has no equivalent, and editing someone's manifest unasked is not something setup does, so a successful install now says what is still missing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): match the SDK by whole name when checking the manifest A substring match read a related pin such as launchdarkly-server-sdk-otel as the SDK itself, so the warning stayed quiet while the project still lacked the dependency. The whole-name matcher the install-skipping check already uses does the job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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 fbbbd11. Configure here.
…#782) * fix(setup): re-derive entry point and package manager on SDK override Choosing an SDK by hand replaced the detected entry point with that SDK's bare default in the working directory, so a project whose entry file was src/index.js got a second index.js created beside it. The package manager was left describing the language detection guessed first, so a Ruby install never ran bundle add. Both are now re-derived for the chosen SDK from one shared table of entry-point candidates, which detection also reads. Snippet-only SDKs have no entry point, so the final screen no longer names an empty path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(setup): report package-manager confidence and stop guessing silently (#783) * feat(setup): report package-manager confidence and stop guessing silently Detection always returned a manager, so a project that never said which one it used got pip or npm presented as fact. Signals are now split into what the project declares and what it merely implies, and a verdict is definite only when the project names exactly one manager. Recognise the corepack packageManager field, poetry.lock, Pipfile.lock and [tool.pdm], which were previously read as pip or npm. [tool.hatch] is recorded as a signal we cannot act on, since hatch has no dependency-add command. `setup install` now reads the project when --package-manager is omitted, instead of defaulting to pip or npm whatever the lockfiles say, and fails with the candidate list when the project is ambiguous rather than picking for the user. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): match nested tool tables and let a committed manager win Real pyproject files rarely carry a bare [tool.x] header, so matching only that left the poetry, uv, pdm and hatch signals near-dead: a hatch project configures [tool.hatch.build], not [tool.hatch]. Nested tables now count, with the trailing delimiter required so [tool.uv] does not match [tool.uvicorn]. A manager the project committed to now settles the verdict even when an unactionable tool is also configured. hatchling is a common build backend for uv and poetry projects, and uv can add the dependency whoever builds the wheel; previously those projects were marked ambiguous and install refused to run. Recognise pdm.lock, so a PDM project that commits only its lockfile is still identified as one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): require a version in the packageManager field pnpm refuses to run at all when package.json names it without a version — "No version specified for pnpm in packageManager" — so treating a versionless field as the project's declared manager routed the user into a command that cannot work. Such a field is no longer a declaration: the lockfiles decide, or the user is asked. When a manager still refuses for that reason, say so. The manifest is malformed rather than the command wrong, and repairing someone's manifest is not ours to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): require one exact version in the packageManager field Corepack accepts only an exact version, so a range is refused outright — "Invalid package manager specification in package.json (pnpm@^11.13.0); expected a semver version" — as is a missing version. Treating either as the project's declared manager routed the user into a command that cannot run. Only an exact MAJOR.MINOR.PATCH, optionally with prerelease or build metadata, now counts; anything else leaves the lockfiles to decide or the user to be asked. When a manager refuses for that reason, say which part is wrong. The manifest is malformed rather than the command, and repairing someone's manifest is not ours to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): read pyproject.toml as TOML to find configured tools Matching [tool.<name>] as text counted comments and strings, so a uv project whose comment mentioned the tool it migrated away from was marked ambiguous and install refused to run. Reading the declared tables instead means only a declaration counts, and nested tables need no special case: TOML creates the parent implicitly, so [tool.hatch.build] alone still declares hatch. A file we cannot parse declares nothing, which leaves the project ambiguous and the user asked — the honest answer when we cannot read what manages it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(setup): read pyproject.toml once per detection Detection parsed the file twice for a Python project: once for the detector's own package manager and again for the confidence verdict. The verdict is now the single source for the languages it models, and the detector leaves the field to it. A language it does not model, such as Java's maven versus gradle, keeps the answer its detector gives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): stop misreading other ecosystems' install errors The packageManager guidance matched a missing or non-semver version anywhere in an install failure, so a gem, a Python package or a Go module reporting either phrase had its real error replaced by advice about a file it does not have. The output has to name package.json, which both corepack refusals do. Say what was actually found when a project is set up for more than one manager: a Pipfile and a [tool.*] table count as commitments too, so naming lockfiles sent the reader looking for files that are not there. Note on stderr when no --package-manager was given, since that used to fall through to npm or pip and now reads the project. Callers parsing output are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(setup): ask which package manager to use when the project is ambiguous (#784) * feat(setup): report package-manager confidence and stop guessing silently Detection always returned a manager, so a project that never said which one it used got pip or npm presented as fact. Signals are now split into what the project declares and what it merely implies, and a verdict is definite only when the project names exactly one manager. Recognise the corepack packageManager field, poetry.lock, Pipfile.lock and [tool.pdm], which were previously read as pip or npm. [tool.hatch] is recorded as a signal we cannot act on, since hatch has no dependency-add command. `setup install` now reads the project when --package-manager is omitted, instead of defaulting to pip or npm whatever the lockfiles say, and fails with the candidate list when the project is ambiguous rather than picking for the user. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): match nested tool tables and let a committed manager win Real pyproject files rarely carry a bare [tool.x] header, so matching only that left the poetry, uv, pdm and hatch signals near-dead: a hatch project configures [tool.hatch.build], not [tool.hatch]. Nested tables now count, with the trailing delimiter required so [tool.uv] does not match [tool.uvicorn]. A manager the project committed to now settles the verdict even when an unactionable tool is also configured. hatchling is a common build backend for uv and poetry projects, and uv can add the dependency whoever builds the wheel; previously those projects were marked ambiguous and install refused to run. Recognise pdm.lock, so a PDM project that commits only its lockfile is still identified as one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): require a version in the packageManager field pnpm refuses to run at all when package.json names it without a version — "No version specified for pnpm in packageManager" — so treating a versionless field as the project's declared manager routed the user into a command that cannot work. Such a field is no longer a declaration: the lockfiles decide, or the user is asked. When a manager still refuses for that reason, say so. The manifest is malformed rather than the command wrong, and repairing someone's manifest is not ours to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): require one exact version in the packageManager field Corepack accepts only an exact version, so a range is refused outright — "Invalid package manager specification in package.json (pnpm@^11.13.0); expected a semver version" — as is a missing version. Treating either as the project's declared manager routed the user into a command that cannot run. Only an exact MAJOR.MINOR.PATCH, optionally with prerelease or build metadata, now counts; anything else leaves the lockfiles to decide or the user to be asked. When a manager refuses for that reason, say which part is wrong. The manifest is malformed rather than the command, and repairing someone's manifest is not ours to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): read pyproject.toml as TOML to find configured tools Matching [tool.<name>] as text counted comments and strings, so a uv project whose comment mentioned the tool it migrated away from was marked ambiguous and install refused to run. Reading the declared tables instead means only a declaration counts, and nested tables need no special case: TOML creates the parent implicitly, so [tool.hatch.build] alone still declares hatch. A file we cannot parse declares nothing, which leaves the project ambiguous and the user asked — the honest answer when we cannot read what manages it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(setup): read pyproject.toml once per detection Detection parsed the file twice for a Python project: once for the detector's own package manager and again for the confidence verdict. The verdict is now the single source for the languages it models, and the detector leaves the field to it. A language it does not model, such as Java's maven versus gradle, keeps the answer its detector gives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): stop misreading other ecosystems' install errors The packageManager guidance matched a missing or non-semver version anywhere in an install failure, so a gem, a Python package or a Go module reporting either phrase had its real error replaced by advice about a file it does not have. The output has to name package.json, which both corepack refusals do. Say what was actually found when a project is set up for more than one manager: a Pipfile and a [tool.*] table count as commitments too, so naming lockfiles sent the reader looking for files that are not there. Note on stderr when no --package-manager was given, since that used to fall through to npm or pip and now reads the project. Callers parsing output are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(setup): ask which package manager to use when the project is ambiguous A project with lockfiles for two managers, or none at all, has no answer we can read off disk, and picking one is how a yarn project gets installed with npm. The wizard now asks, and says why it is asking. Installed managers are listed first and the cursor starts on one, but an uninstalled manager stays selectable: the choice is the user's and the install step already warns rather than installing tooling. Projects that state their manager go straight to the plan. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): keep the package-manager picker inside the terminal The screen draws a title, the reason for asking and a key hint around the list, but the list was sized to the whole window, so the hint — including how to go back — was pushed off the bottom at every terminal size. The list now leaves room for that chrome, and the list's own help line goes away since the screen prints its own. Below fourteen rows the reason is dropped: at that size the question and the choices matter more than the explanation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): show each screen's key hints once, inside the list The project and environment screens printed a footer of key hints while the list below already rendered its own help, so every instruction appeared twice. The wizard's own bindings now go into the list's help line, which is the single place a screen states them, and the footers are gone. The package-manager picker follows the same shape, and its height reserve is retuned for the help line the list now draws, including the extra row that line takes when the terminal is narrower than it is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): wrap the plan steps to the terminal width A step naming an absolute entry-point path alongside the warning that no entry file was found runs well past a narrow terminal, and overflowing there hides the warning the step exists to give. Steps now wrap, with what wraps indented under the number so a step still reads as one item. The screen that names the injected file wraps for the same reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): stop padding pushing the injected file path off screen Wrapping pads every line to the full width, so the newline left inside the wrapped lead put a whole row of spaces in front of the file path and carried it past the edge of the terminal. The newline now sits outside the wrap, and the closing instruction wraps too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(setup): stop the package-manager list quitting on esc The list inherits the same quit binding as the others, so esc arriving on its own ended the session from the picker. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(setup): say the verify step waits for the app as well as the SDK Verification cannot succeed until the user's application is running, so naming only the SDK left it unclear whether anything was expected of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
A successful install can leave the user something to do — the SDK not recorded in requirements.txt — but only the verification-succeeded screen said so. The flow also ends on "manual SDK setup required" and on a verification timeout, both reachable after a real install, and on those the note was lost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Requirements
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.
Note
Overview
Replaces the old
setupentry with a guided Bubble Tea wizard that detects language/framework, lets the user pick project/env/SDK (and package manager when ambiguous), previews a plan, then installs the SDK, createsmy-new-flag, injects init code, and verifies the SDK is active. The previous wizard is kept as hidden, deprecatedldcli quickstart.Hidden
setup detect|install|initsubcommands expose the same steps for scripts (JSON/plaintext). Detection prefers backend manifests over a rootpackage.json, distinguishes found vs guessed entry points, and refuses to guess an ambiguous package manager. Install/init fall back to copyable commands/snippets;ccopies via OS clipboard or OSC 52 (SSH skips the remote clipboard).Also paginates project/env lists, and creates the demo flag with client-side and mobile availability so browser/mobile SDKs can evaluate it. CI now pins Go from
go.modso golangci-lint stays compatible.Reviewed by Cursor Bugbot for commit 1fa0426. Bugbot is set up for automated code reviews on this repo. Configure here.