feat(setup): report package-manager confidence and stop guessing silently - #783
Open
ffantl-ld wants to merge 7 commits into
Open
feat(setup): report package-manager confidence and stop guessing silently#783ffantl-ld wants to merge 7 commits into
ffantl-ld wants to merge 7 commits into
Conversation
3 tasks
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 18, 2026 15:22
282f963 to
6df6b57
Compare
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 18, 2026 15:29
6df6b57 to
3cfd103
Compare
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 19, 2026 14:12
055976e to
7fef87c
Compare
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 19, 2026 15:51
7fef87c to
87e9ed0
Compare
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 19, 2026 16:09
87e9ed0 to
9246e77
Compare
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 19, 2026 16:52
9246e77 to
b4e5252
Compare
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 19, 2026 17:39
b4e5252 to
f0a249f
Compare
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 f0a249f. Configure here.
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 19, 2026 18:18
f0a249f to
c10867f
Compare
cspath1
approved these changes
Aug 19, 2026
…ntly 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>
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>
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>
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>
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>
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>
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>
ffantl-ld
force-pushed
the
feat/setup-pm-confidence
branch
from
August 19, 2026 21:14
acfb88b to
6446556
Compare
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
Steps 1–3 of the package-manager selection design. Stacked on #782, which owns
PackageManagerFor. The wizard picker (step 4) is a follow-up.Describe the solution you've provided
definite/ambiguousverdict. Detection previously always returned a manager, so a project that never said which one it used gotpipornpmpresented as fact.packageManagerfield (which outranks lockfiles, being the most explicit statement a project can make),poetry.lock,Pipfile.lock,package-lock.json,npm-shrinkwrap.json, and[tool.pdm].[tool.hatch]as a signal we cannot act on — hatch has no dependency-add command — so a hatch project stays ambiguous instead of silently falling through to pip.setup installreads the project when--package-manageris omitted, rather than defaulting to pip/npm whatever the lockfiles say. On an ambiguous project it fails with the candidate list instead of choosing for the user.setup detectreports the verdict, and its JSON addspackage_manager_candidateswith the command each would run and whether it is installed.Describe alternatives you've considered
yarn.lockandpackage-lock.jsonhas no correct guess, and todaydetectNodePMreturnsyarnpurely because it checks first. No amount of signal tuning fixes a project that contradicts itself.pnpminstalled says nothing about whether this repo uses it, and it would give two developers different answers for the same project. Installed state orders the list and nothing more.setup installrather than failing. Rejected: these subcommands are the documented engine for external agents, and a silent wrong manager is worse than an error.Additional context
PackageManagerCandidatesis deliberately not onDetectResult.installeddescribes the machine rather than the project, so keeping it out leaves detection deterministic and its tests machine-independent; thedetectcommand adds candidates to its own JSON payload for callers that need them.Installedtracks the executable the resolved command actually runs, not the candidate label — thepipcandidate reports installed when onlypip3exists, because its command ispip3 install.DetectResult.PackageManageris still whatever the language detector produced. Those detectors know about managers this model does not, such as maven versus gradle.Testing approaches
go test ./...passes.[tool.uv],[tool.pdm],Gemfile) and ambiguous ones (conflicting lockfiles, barepackage.json, PEP 621pyproject.tomlonly,requirements.txtonly, hatch, gemspec without a Gemfile).pnpmon a stubbed PATH, a barepackage.jsonstays ambiguous and the name staysnpm.TestFileDetector_ProjectShapesclears the confidence fields before comparing, since those shapes assert language/SDK/entry point and confidence has its own tests.pnpm@9.1.0→pnpm(was npm);yarn.lock+package-lock.json→ flagged uncertain naming both (was silently yarn);[tool.pdm]→pdm(was pip);[tool.hatch]→ uncertain (was pip).setup installfails on the conflicting project and resolvesuv addin auv.lockproject where it previously ranpip3 install.Note
Overview
Setup no longer treats
npm/pipas facts when the repo never declared a manager. Detection now separates declared signals (corepackpackageManager, lockfiles,[tool.*]inpyproject.toml) from guesses and addspackage_manager_confidence(definitevsambiguous) plus a user-facing reason onDetectResult.setup detectmarks uncertain managers in plaintext and, in JSON, addspackage_manager_candidates(command + whether the tool is on PATH) when ambiguous—without putting machine-specificinstalledon the core detect struct.setup installwithout--package-managerreads the project viaPackageManagerChoiceFor; ambiguous projects error with candidate names (not installed tools flagged). When auto-selection succeeds, a one-time stderr note explains the behavior change from blind npm/pip defaults. Install plaintext/JSON can surfaceWarningfrom pip installs that don’t updaterequirements.txt, and the wizard success screen shows that note.Wizard SDK override re-derives entry point and package manager for the chosen SDK (e.g. Ruby gets
gem/bundle, not Node’spnpm) using sharedEntryPointFor/PackageManagerFortables.Installer adds PDM support, parses
pyproject.tomlwith TOML (not substring matching), handles invalid corepack specs in failure text, and improves venv pip path resolution on Windows.Tests add
CallCmdCapturingStderrand broad confidence/signal coverage.Reviewed by Cursor Bugbot for commit 6446556. Bugbot is set up for automated code reviews on this repo. Configure here.