Description
Several language servers declare their project root with a glob pattern rather than a concrete filename:
- Haskell (
haskell-language-server): *.cabal
- Terraform (
terraform-ls): *.tf
- Julia (
julia-ls): *.jl
- Swift / Xcode (
sourcekit-lsp): *.xcodeproj, *.xcworkspace
Filesystem.up (used by NearestRoot in packages/opencode/src/lsp/server.ts) only probes each candidate directory with a literal exists(join(dir, target)). A wildcard target such as *.cabal is never a literal filename, so the probe always returns false. The server therefore never finds the real project root and silently falls back to the workspace root (ctx.directory).
Impact
For multi-package Haskell projects, Terraform modules, Julia packages, and Xcode projects living in subdirectories, the LSP attaches to the wrong root. That breaks go-to-definition across packages, diagnostics, and module-level features — the server effectively cannot tell where the project actually is.
Steps to reproduce
- Open a Haskell project with a cabal package nested under
packages/mylib/mylib.cabal.
- Inspect the root that
haskell-language-server is given.
- It resolves to the workspace root instead of
packages/mylib.
Affected servers (in lsp/server.ts)
HLS — NearestRoot(["stack.yaml","cabal.project","hie.yaml","*.cabal"])
TerraformLS — NearestRoot([".terraform.lock.hcl","terraform.tfstate","*.tf"])
JuliaLS — NearestRoot(["Project.toml","Manifest.toml","*.jl"])
SourceKit — NearestRoot(["Package.swift","*.xcodeproj","*.xcworkspace"])
I will follow up with a PR that makes Filesystem.up scan the directory for wildcard targets (using include: "all" so directory-style markers like *.xcodeproj are also matched) and adds regression tests.
Description
Several language servers declare their project root with a glob pattern rather than a concrete filename:
haskell-language-server):*.cabalterraform-ls):*.tfjulia-ls):*.jlsourcekit-lsp):*.xcodeproj,*.xcworkspaceFilesystem.up(used byNearestRootinpackages/opencode/src/lsp/server.ts) only probes each candidate directory with a literalexists(join(dir, target)). A wildcard target such as*.cabalis never a literal filename, so the probe always returns false. The server therefore never finds the real project root and silently falls back to the workspace root (ctx.directory).Impact
For multi-package Haskell projects, Terraform modules, Julia packages, and Xcode projects living in subdirectories, the LSP attaches to the wrong root. That breaks go-to-definition across packages, diagnostics, and module-level features — the server effectively cannot tell where the project actually is.
Steps to reproduce
packages/mylib/mylib.cabal.haskell-language-serveris given.packages/mylib.Affected servers (in
lsp/server.ts)HLS—NearestRoot(["stack.yaml","cabal.project","hie.yaml","*.cabal"])TerraformLS—NearestRoot([".terraform.lock.hcl","terraform.tfstate","*.tf"])JuliaLS—NearestRoot(["Project.toml","Manifest.toml","*.jl"])SourceKit—NearestRoot(["Package.swift","*.xcodeproj","*.xcworkspace"])I will follow up with a PR that makes
Filesystem.upscan the directory for wildcard targets (usinginclude: "all"so directory-style markers like*.xcodeprojare also matched) and adds regression tests.