Skip to content

chore(setup): add SDK/project detection library - #749

Open
ffantl-ld wants to merge 1 commit into
setup-ldfrom
ffantl/setup-ld/1-detector
Open

chore(setup): add SDK/project detection library#749
ffantl-ld wants to merge 1 commit into
setup-ldfrom
ffantl/setup-ld/1-detector

Conversation

@ffantl-ld

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

Copy link
Copy Markdown
Contributor

Describe the solution you've provided

First layer of the guided setup command: the internal/setup detection library. Inspects a project directory and identifies the language/package manager and the most likely LaunchDarkly SDK (Detector interface, FileDetector, KnownSDKs).

Pure library with no consumer yet — the setup command wires it up later in this stack.

Related issues

Part of the setup-ld feature. Stacked PR — base is setup-ld.

Requirements

  • I have added test coverage for new or changed functionality

Note

Low Risk
New read-only local filesystem logic with no CLI integration yet; mis-detection could suggest the wrong SDK later but does not touch auth or production paths in this PR.

Overview
Adds internal/setup, a filesystem-based library for the upcoming guided ldcli setup flow. A Detector interface and FileDetector scan a project directory and return language, package manager, framework (when applicable), a recommended LaunchDarkly sdk_id, and a suggested entry-point path.

Node detection parses package.json dependencies (including devDependencies), infers npm/yarn/pnpm/bun from lockfiles, and maps frameworks with explicit precedence (e.g. Next.js → node-server, React Native before React, browser stacks → js-client-sdk). Go, Python, Ruby, Java (vs Android via AndroidManifest.xml), Swift, and .NET use conventional project markers. Unknown layouts error with guidance to pass --sdk-id. KnownSDKs lists SDKs for manual selection; StubDetector remains an unimplemented placeholder.

Broad unit tests cover stacks, edge cases (malformed package.json, entry-point fallbacks), and Ruby-specific fixtures.

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
if result := detectDotnet(dir); result != nil {
return result, nil
}
return nil, errors.New("could not detect project language from directory; try specifying --sdk-id manually")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

"could not detect project language from directory")

Should probably mean "Hey, here's a link to the docs and a blurb about how to do this install manually"

If we're doing a hybrid approach to setup, then we should definitely

@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

@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 5 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 3a1d4dd. Configure here.

return &DetectResult{
Language: "Java",
PackageManager: "gradle",
SDKID: "android-client-sdk",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wrong Android SDK identifier

Medium Severity

Detected and listed Android SDK ID is android-client-sdk, but every other ID in KnownSDKs matches the established SDK IDs used by sdk_instructions and quickstart (where Android is android from android.md). This mismatch will fail lookups against sdkmeta or instruction files when setup is wired up.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3a1d4dd. Configure here.

"src/index.ts", "src/index.js",
"pages/index.tsx", "pages/index.ts", "pages/index.js",
"index.js",
})),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Next.js entry points outdated

Medium Severity

Next.js entry-point candidates only cover Pages Router paths (pages/index.*, src/index.*) and fall back to index.js. App Router defaults from create-next-app use app/page.tsx / src/app/page.tsx, so detection suggests a nonexistent file for the common modern layout.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3a1d4dd. Configure here.

PackageManager: "gradle",
SDKID: "android-client-sdk",
EntryPoint: filepath.Join(dir, "app/src/main/java/MainActivity.java"),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Android entry path mismatch

Medium Severity

Android detection accepts manifests under either app/src/main/ or src/main/, but always returns the hardcoded entry point app/src/main/java/MainActivity.java. That path is wrong for the non-app/ layout and for typical Kotlin projects (even android.md references MainActivity.kt).

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3a1d4dd. Configure here.

EntryPoint: filepath.Join(dir, firstExistingIn(dir, []string{
"config.ru", "app.rb", "main.rb",
})),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ruby package manager incorrect

Medium Severity

Ruby projects detected via Gemfile / Gemfile.lock report PackageManager as gem. Project-level installs for those indicators use Bundler (bundle add), not gem install. Node carefully distinguishes npm/yarn/pnpm/bun, so this value is inconsistent and will drive the wrong install command later.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3a1d4dd. Configure here.

"src/main.py", "manage.py", "app.py", "main.py",
})),
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Poetry projects reported as pip

Low Severity

Any pyproject.toml (including Poetry, as in the unit test) always sets PackageManager to pip. Install tooling keyed off that field would run pip in Poetry-managed repos instead of poetry add, which is the wrong workflow for those projects.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3a1d4dd. Configure here.

@ffantl-ld
ffantl-ld requested review from a team and removed request for Vadman97 July 28, 2026 16:37
@erangeles

erangeles commented Jul 28, 2026

Copy link
Copy Markdown

@ffantl-ld there is some valid bug bot issues flagged, wanted to make sure you were aware of them (either to fix now or punt on)

// firstExistingIn returns the first candidate that exists as a file in dir,
// or the last candidate if none exist (as a suggested path).
// Returns an empty string if candidates is empty.
func firstExistingIn(dir string, candidates []string) string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: we should return whether the entry point actually exists so downstream steps can prompt the user instead

type DetectResult struct {
      // ...
      EntryPoint       string `json:"entry_point"`
      EntryPointExists bool   `json:"entry_point_exists"`
}

// firstExistingIn returns the first candidate that exists in dir and true,
// or the last candidate and false as a suggestion if none exist.
func firstExistingIn(dir string, candidates []string) (string, bool) {
      if len(candidates) == 0 {
              return "", false
      }
      for _, c := range candidates {
              if _, err := os.Stat(filepath.Join(dir, c)); err == nil {
                      return c, true
              }
      }
      return candidates[len(candidates)-1], false
}

Then the wizard can branch: exists → inject; doesn't → ask the user for the entry file (or show the snippet).

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