Adds a second reproducer covering the 'forever' branch of issue #22872:
when Pyright.spawn calls Npm.which('pyright') and the npm registry is
unreachable (sandboxed container), arborist.reify blocks indefinitely
with no timeout.
Changes:
- Adds optional Info.spawnEffect alongside the existing async Info.spawn.
spawnEffect returns an Effect that can yield from Npm.Service, making
npm lookups injectable for tests.
- Migrates Pyright to use spawnEffect, pulling the venv probing logic
into a reusable pyrightVenvInitialization helper. The legacy async
spawn stays for backwards compatibility.
- Threads Npm.Service through LSP.layer so getClients captures a stable
reference and uses it for any server that provides spawnEffect.
- Adds test/tool/write-lsp-spawn-hang.test.ts — mocks Npm.Service.which
with Effect.never and asserts the write tool still returns in < 10s.
Fails today (hangs forever); the fix must bound the touchFile tail
so the tool cannot wait on a wedged LSP spawn.
The two reproducers now cover both hang branches:
- write-lsp-hang.test.ts: 45s LSPClient.create initialize timeout
- write-lsp-spawn-hang.test.ts: unbounded Npm.which arborist.reify