Skip to content

feat: default LSP off, scope TS-ecosystem to nearest package#236

Merged
Astro-Han merged 19 commits into
devfrom
worktree-claude+issue-232-lsp
Apr 26, 2026
Merged

feat: default LSP off, scope TS-ecosystem to nearest package#236
Astro-Han merged 19 commits into
devfrom
worktree-claude+issue-232-lsp

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Apr 25, 2026

Copy link
Copy Markdown
Owner

Summary

Make LSP opt-in (default off) with a Settings → General toggle, and prevent the TypeScript LSP from blowing up to monorepo-root scope when active.

  • New Settings.Service holds lspEnabled in an Effect Ref; LSP and ToolRegistry layers read it at state-init time so the lsp tool and server registry both flip together.
  • Renderer toggle calls window.api.setLspEnabled → Electron main IPC handler enters each active Instance scope, runs LSP.shutdownAll (only on going false), then LSP.invalidate and ToolRegistry.invalidate so caches and tsserver processes are reset per-project.
  • New JavascriptPackageRoot helper prepends tsconfig.json and package.json to lockfile root markers, applied to TypeScript / Vue / ESLint / Svelte / Astro / YamlLS.
  • read.ts no longer triggers lsp.touchFile on read (aligns with Codex CLI / Gemini CLI / Claude Code baseline).
  • LSPServer.Info.packages becomes the single source of truth for npm-installed servers; install failures from Npm.add are classified as transient and skip s.broken poison so the next edit can retry.
  • New lsp.server.install.failed bus event surfaces an error toast in the renderer.

Why

Issue #232: opening PawWork's own monorepo caused tsserver to balloon to ~3.4 GB / 97% CPU because the TS server treated the worktree root (lockfile) as the project root. Two failure modes were tangled: (1) LSP cost was unconditional even when users did not need diagnostics, (2) TS root resolution found the lockfile before the nearest tsconfig.json. The toggle gives users the off switch; the NearestRoot fix keeps the on path bounded to one package.

Related Issue

Closes #232

How To Verify

bun --cwd packages/opencode run typecheck
bun --cwd packages/opencode test
bun --cwd packages/app run typecheck
bun --cwd packages/app test
bun --cwd packages/desktop-electron run typecheck
bun --cwd packages/desktop-electron test

Manual smoke (run on macOS, in bun run dev:desktop):

Screenshots or Recordings

Settings → General toggle row added at the bottom of the existing list; copy is Language Server Protocol (LSP) / Detect type errors and symbol references when editing code. No layout reflow.

Checklist

  • I linked the related issue, or stated why there is no issue
  • This PR has type, scope, and priority labels, or I requested maintainer labeling
  • I listed the relevant verification steps, including tests when behavior changed
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for desktop, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, or generated/local file changes when relevant
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a toggleable LSP (Language Server Protocol) setting in General settings to enable/disable language server functionality
    • Added error notifications displaying package name and error details when LSP server installation fails
  • Improvements

    • LSP feature is now configurable via settings instead of an experimental flag

Prepend tsconfig.json and package.json to lockfile root markers via
new JavascriptPackageRoot helper, applied to TypeScript, Vue, ESLint,
Svelte, Astro, and YamlLS. Prevents tsserver from analyzing the entire
monorepo when only one package is in use.

Filter Object.values(LSPServer) iteration in lsp/index.ts so the new
helper export does not leak into the server registry.

Refs #232
Reading a file no longer triggers lsp.touchFile. Aligns with industry
baseline (Codex CLI, Gemini CLI, and Claude Code all skip this in
their read paths). LSP work now happens only on edit/write/apply_patch
where diagnostics are actually consumed.

Refs #232
New service holds lspEnabled in an Effect Ref, defaulting to false.
LSP and ToolRegistry layers will consume this value at state init in
subsequent commits; the PawWork-side coordinator (Task 10) sets the
Ref on toggle changes.

Public helpers go through makeRuntime (sharing the singleton memoMap
with AppRuntime) so the Ref is one cell across all consumers, while
avoiding a circular import between Settings and AppRuntime.

Refs #232
Replace cfg.lsp reads in lsp/index.ts state init with a Settings
service read. PawWork is the sole source of truth for LSP enablement;
opencode user config lsp records (custom server entries and per-server
disabled flags) are no longer consulted at runtime. The cfg.lsp schema
stays in place for upstream compatibility but is dead code under PawWork.

Also includes:
- Existing lifecycle/index LSP tests opt back in via Settings.setLspEnabled(true)
  in beforeEach since the default flipped to off.
- CI Windows shard coverage updated to include test/settings.test.ts in
  the opencode-config-project shard (the contract test enforces that
  every test file is covered by exactly one shard).

Refs #232
Drop the OPENCODE_EXPERIMENTAL_LSP_TOOL flag; gate lsp tool inclusion
in builtin[] by lspEnabled at state-init time. All registry enumeration
paths (tools, all, ids, named) read the same cached builtin[], so they
stay consistent automatically. Layer dependency on Settings is wired
through ToolRegistry.defaultLayer; manual session-test layer composers
also provide Settings.defaultLayer.

Refs #232
shutdownAll drains s.spawning Promises (raw JS Promises wrapped in
Effect.promise that scope finalizers cannot cancel) and calls
client.shutdown on every entry in s.clients. invalidate clears the
InstanceState cache so the next access re-runs state init with
fresh Settings.lspEnabled.

Both methods are exposed via Service.of and as public re-exports so
the PawWork coordinator (Task 10) can call them directly. Mock LSP
services in session tests get matching no-op stubs.

Refs #232
Clear the InstanceState cache so the next tools()/ids() call re-runs
state init with fresh Settings.lspEnabled. Used by the PawWork
coordinator (Task 10) after settings flips to make the lsp tool
appear or disappear without a session restart.

Refs #232
Add packages field to LSPServer.Info as the single source of truth
for npm packages each server downloads. Export LSP_SERVER_PACKAGES
set derived from those entries.

In schedule()'s .catch branch, suppress s.broken.add when the error
is Npm.InstallFailedError. Install failures (network/disk) are
transient and should be retried on the next edit. Genuine spawn errors
(process crash, init handshake) and missing tools (Module.resolve null)
still poison s.broken.

Refs #232
Define LSP.Event.InstallFailed via BusEvent.define so the type is
included in the SSE event schema. Npm.add publishes the event when
the failed package is in LSP_SERVER_PACKAGES; the lookup is performed
through dynamic import to avoid an npm <-> lsp circular module load.

App-side global-sync subscribes to the new event and shows an error
toast (i18n keys land in Task 11). The SDK's generated event union
does not yet include the new type, so the comparison casts to string;
the cast can drop after the next sdk regen.

Refs #232
Renderer settings store change calls window.api.setLspEnabled. Main
process IPC handler "lsp-set-enabled" coordinates the runtime-side
flip in order:
1. Settings.setLspEnabled writes the Ref
2. On going false, LSP.shutdownAll kills running processes
3. LSP.invalidate forces re-init with the new value
4. ToolRegistry.invalidate refreshes the lsp tool visibility

The opencode runtime is in-process with Electron main, so the
coordinator imports the namespaces directly via virtual:opencode-server.
Settings/LSP/ToolRegistry are added to opencode's node entry exports.

Refs #232
Default off. Toggling persists to settings store and triggers the
runtime-side coordinator (Task 10) via window.api.setLspEnabled to
invalidate caches and shut down processes.

Toggle row title is "Language Server Protocol (LSP)" / 语言服务器协议(LSP)
with the description framed in user-task terms ("Detect type errors and
symbol references when editing code"). Toast strings cover the
lsp.server.install.failed event consumer in global-sync.

Refs #232
LSP.invalidate and ToolRegistry.invalidate read InstanceState.directory,
which requires an Instance ALS context. The IPC handler runs outside
any Instance, so calls were throwing LocalContext.NotFound.

Expose Instance.directories() to enumerate active instance roots, then
loop them in the lsp-set-enabled handler and run shutdownAll/invalidate
inside Instance.provide so each project's caches and processes are
reset.

Refs #232
@Astro-Han Astro-Han added enhancement New feature or request P1 High priority harness Model harness, prompts, tool descriptions, and session mechanics labels Apr 25, 2026
@coderabbitai

coderabbitai Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 57 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 57 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7d72b232-d18c-413f-978d-b0465b5b9526

📥 Commits

Reviewing files that changed from the base of the PR and between 3653f0d and c082d77.

📒 Files selected for processing (9)
  • packages/app/src/context/settings.tsx
  • packages/app/src/i18n/en.ts
  • packages/app/src/i18n/zh.ts
  • packages/desktop-electron/src/main/ipc.ts
  • packages/opencode/src/lsp/index.ts
  • packages/opencode/src/lsp/server.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/settings.test.ts
📝 Walkthrough

Walkthrough

This PR implements user-controlled LSP (Language Server Protocol) enablement, adding a settings toggle to control language server activation across the application. It includes Settings module infrastructure, Electron IPC bridge implementation, LSP service lifecycle management (shutdown/invalidation), LSP install failure notifications, and comprehensive test coverage. Removes automatic LSP warming from file reads.

Changes

Cohort / File(s) Summary
CI & Test Sharding
.github/workflows/ci.yml, packages/opencode/test/github/ci-workflow.test.ts
Updates Windows opencode-config-project shard's bun test command to include test/settings.test.ts in CI coverage.
UI Settings Layer
packages/app/src/components/settings-general.tsx, packages/app/src/i18n/en.ts, packages/app/src/i18n/zh.ts
Adds LSP toggle UI row with localized title/description; adds English and Chinese translation strings for settings UI and LSP install failure toast notifications (with {pkg} and {error} placeholders).
Settings Service & Context
packages/opencode/src/settings/index.ts, packages/app/src/context/settings.tsx
Introduces Effect-based Settings module with in-memory lspEnabled boolean (default false), provides async runtime wrappers (lspEnabled() / setLspEnabled(value)), and integrates with renderer settings context to expose API via window.api.
Electron IPC & Type Bridges
packages/desktop-electron/src/preload/index.ts, packages/desktop-electron/src/preload/types.ts, packages/desktop-electron/src/main/env.d.ts, packages/desktop-electron/src/main/ipc.ts, packages/app/src/app.tsx
Adds setLspEnabled IPC endpoint and preload method; extends ElectronAPI and global Window.api types; handler reinitializes LSP/tool registry across all instances and conditionally shuts down LSP on disable.
LSP Service Lifecycle & Error Handling
packages/opencode/src/lsp/index.ts, packages/opencode/src/lsp/server.ts, packages/opencode/src/context/global-sync.tsx
Switches LSP enablement source from Flag to Settings; adds LSP.shutdownAll() and LSP.invalidate() capabilities; treats npm install failures specially (no "broken" mark); emits lsp.server.install.failed event with package/error details; introduces JS workspace marker root and package metadata tracking on servers.
Tool Registry & Package Integration
packages/opencode/src/tool/registry.ts, packages/opencode/src/tool/read.ts, packages/opencode/src/npm/index.ts
Tool registry now gates LSP tool inclusion via Settings.lspEnabled(); invalidation support added; removes LSP warming hook from ReadTool; npm module emits LSP install failure events when registering LSP-related packages.
Module Exports & Public APIs
packages/opencode/src/node.ts, packages/opencode/src/project/instance.ts, packages/opencode/src/effect/app-runtime.ts
Exports Settings, LSP, ToolRegistry, and Instance from node.ts entrypoint; adds Instance.directories() accessor; integrates Settings layer into app runtime.
LSP Service Tests
packages/opencode/test/lsp/lspEnabled.test.ts, packages/opencode/test/lsp/server.test.ts, packages/opencode/test/lsp/index.test.ts, packages/opencode/test/lsp/lifecycle.test.ts
New suite validates LSP gating, shutdown/invalidate behavior, LSP_SERVER_PACKAGES membership, and install failure handling; existing tests wrapped with beforeEach/afterEach to toggle LSP settings.
Settings & Tool Registry Tests
packages/opencode/test/settings.test.ts, packages/opencode/test/tool/registry.test.ts, packages/opencode/test/tool/read.test.ts
New Settings service tests verify default false state and persistence; registry tests validate LSP tool inclusion/exclusion based on settings and invalidation behavior; source inspection test confirms read.ts no longer calls LSP warm hook.
Session & Integration Tests
packages/opencode/test/session/prompt-effect.test.ts, packages/opencode/test/session/snapshot-tool-race.test.ts
Test harnesses updated to include Settings layer and mock new LSP lifecycle methods (shutdownAll, invalidate) as no-ops.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SettingsUI as Settings UI<br/>(Renderer)
    participant IPC as Electron IPC<br/>(Bridge)
    participant MainProc as Main Process
    participant Settings as Settings<br/>Service
    participant LSP as LSP<br/>Service
    participant ToolReg as Tool<br/>Registry
    participant Instance as Instance<br/>(Per-project)

    User->>SettingsUI: Toggle LSP Switch
    SettingsUI->>IPC: invoke("lsp-set-enabled", true/false)
    IPC->>MainProc: ipcMain.handle received
    MainProc->>Settings: Settings.setLspEnabled(value)
    Settings->>Settings: Update internal Ref<boolean>
    MainProc->>Instance: Iterate all directories
    Instance->>LSP: LSP.invalidate()<br/>(force re-init)
    LSP->>LSP: Clear instance state
    alt value is false
        LSP->>LSP: LSP.shutdownAll()<br/>(stop all servers)
    end
    MainProc->>ToolReg: ToolRegistry.invalidate()
    ToolReg->>ToolReg: Re-compute tool list<br/>based on new<br/>settings.lspEnabled()
    MainProc->>IPC: Return Promise<void>
    IPC->>SettingsUI: Resolve
    SettingsUI->>User: UI reflects new state
Loading
sequenceDiagram
    participant npm as npm<br/>Module
    participant LSPPkgs as LSP_SERVER_<br/>PACKAGES
    participant Bus as Event Bus
    participant Renderer as Renderer<br/>(Toast UI)

    npm->>npm: add(package) called
    npm->>npm: Install fails
    npm->>LSPPkgs: isInstallFailure?<br/>Check if pkg in set
    alt Package is LSP server
        npm->>Bus: Emit LSP.Event.InstallFailed<br/>{ pkg, error }
        Bus->>Renderer: Dispatch event
        Renderer->>Renderer: Render error toast<br/>with localized msg
    else Not an LSP package
        npm->>npm: Throw InstallFailedError
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feature, settings, desktop, lsp, testing

Poem

🐰 A toggle for the TypeScript flame,
LSP now bends to user's name,
Settings dance, IPC sings,
Shutdown, invalidate—all the things!
No more warming reads with care,
Control returns to the builder's chair. 🎚️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: default LSP off, scope TS-ecosystem to nearest package' accurately summarizes the main changes: making LSP opt-in (default off) and fixing TypeScript root resolution to use the nearest package instead of monorepo root.
Description check ✅ Passed The description comprehensively covers all template sections: Summary explains LSP toggle and TypeScript scoping; Why references issue #232; Related Issue links to #232; How To Verify provides detailed commands and manual smoke tests; Screenshots document UI changes; Checklist is complete.
Linked Issues check ✅ Passed The PR fully addresses issue #232's objectives: LSP is now opt-in (default off), TypeScript root resolution is scoped to nearest package via JavascriptPackageRoot, LSP warming is removed from read path, install failures are transient, and Settings.Service provides user-visible toggle.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the stated objectives in #232: Settings.Service and LSP gating, JavascriptPackageRoot implementation, LSP invalidation/shutdown APIs, npm install failure handling, and test coverage for all modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-claude+issue-232-lsp

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a user-facing toggle for the Language Server Protocol (LSP) within the application settings. Key changes include the addition of a new Settings service to manage the lspEnabled state, IPC handlers to synchronize this setting between the renderer and the main process, and updates to the LSP and Tool Registry services to dynamically enable or disable functionality based on the user's preference. Additionally, the PR includes localized strings for the new settings and error toasts for LSP installation failures. Feedback indicates a synchronization gap where the persisted setting in the renderer is not communicated to the main process upon application startup, potentially leaving the backend out of sync until the setting is manually toggled.

Comment thread packages/app/src/context/settings.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/app/src/context/settings.tsx`:
- Around line 244-248: The setter setLspEnabled currently calls
window.api.setLspEnabled(...) fire-and-forget which can leave UI state
inconsistent if the async IPC rejects; update setLspEnabled to await the promise
returned by window.api.setLspEnabled (or at least attach .then/.catch) and
handle rejection by logging the error and reverting the persisted UI state via
setStore("general", "lspEnabled", ...) (or otherwise synchronizing UI with the
runtime), ensuring no unhandled promise rejections and clear error logging.

In `@packages/app/src/i18n/en.ts`:
- Around line 837-840: The translation key "toast.lsp.installFailed.description"
uses single-brace tokens "{pkg}" and "{error}" but the project expects the
double-brace interpolation format; update the value for
toast.lsp.installFailed.description to use "{{pkg}}" and "{{error}}" instead so
runtime interpolation matches the existing token syntax.

In `@packages/app/src/i18n/zh.ts`:
- Around line 726-729: The toast.lsp.installFailed.description uses single-brace
tokens but the i18n system expects double-brace placeholders; update the value
for "toast.lsp.installFailed.description" to use {{pkg}} and {{error}} (e.g.
"toast.lsp.installFailed.description": "{{pkg}}: {{error}}") so the package and
error values are interpolated correctly by the i18n renderer.

In `@packages/desktop-electron/src/main/ipc.ts`:
- Around line 181-191: The loop that calls Instance.provide for each directory
can abort on the first failure, leaving some directories with stale
LSP/ToolRegistry state; update the logic that iterates Instance.directories() so
each Instance.provide(...) call is isolated (either run all provides with
Promise.allSettled or wrap each iteration in a try/catch) and ensure that on
failure you still continue processing remaining directories and still call
LSP.shutdownAll(), LSP.invalidate(), and ToolRegistry.invalidate() for each
instance as appropriate; reference Instance.directories(), Instance.provide(),
LSP.shutdownAll(), LSP.invalidate(), and ToolRegistry.invalidate() when applying
the change.

In `@packages/opencode/src/lsp/index.ts`:
- Around line 170-180: The LSP gate is effectively default-on because
Settings.defaultLayer seeds the ref with true; update the settings default so
lspEnabled() returns false unless explicitly enabled. Change the seed for
Settings.defaultLayer (the Ref initialized via Ref.make(true) in
packages/opencode/src/settings/index.ts) to Ref.make(false) or otherwise make
Settings.Service/Settings.lspEnabled() consult an opt-in flag (env/CLI) so LSP
is disabled by default; ensure ToolRegistry and any other callers of
Settings.Service (e.g. the LSP instantiation in InstanceState.make<State> and
the other occurrence flagged around line 515) use the revised default behavior.
- Around line 226-233: When a spawn() fails with Npm.InstallFailedError the code
must mark the server as temporarily broken to avoid retriggering installs on
every LSP interaction; update the catch in server.spawn() so that for both
general errors and InstallFailedError you add the server key to s.broken, and
for install failures also set a cooldown (e.g., record a nextRetry timestamp or
schedule removal from s.broken after a short backoff) so subsequent
touchFile/hover/definition calls skip immediate retries; reference s.broken,
server.spawn, and Npm.InstallFailedError when making the change.

In `@packages/opencode/test/lsp/lspEnabled.test.ts`:
- Around line 17-31: The test is manually wiring runtime via Instance.provide
and AppRuntime.runPromise; replace that pattern with the Effect test harness:
rewrite the test to use testEffect(...) with it.live(...) and swap manual
provisioning for provideTmpdirInstance(...) (or provideInstance(...) where
appropriate) so the Effect is run via the test harness and tmpdir/Instance
lifecycle is handled automatically; locate usages of Instance.provide,
AppRuntime.runPromise, Settings.Service, LSP.Service and LSP.touchFile in this
file and replace the outer Instance.provide + AppRuntime.runPromise wrapper with
a testEffect that yields the same Effect body and is provided with
provideTmpdirInstance (or provideInstance) so cleanup and live filesystem
behavior match the suite.
- Around line 39-63: The test enables Settings.lspEnabled but never resets it,
causing later tests to inherit lspEnabled=true; in the finally block after
spy.mockRestore() ensure you reset the shared setting by invoking the Settings
service and calling settings.setLspEnabled(false) (use the same runtime pattern
as the test—e.g., run a short Effect/AppRuntime call that accesses
Settings.Service and calls setLspEnabled(false)) before disposing instances so
the global memoized layer is returned to its original state.

In `@packages/opencode/test/lsp/server.test.ts`:
- Around line 17-25: Replace the manual makeFixture + fs.mkdtempSync and the
direct Instance.provide(...) usage with the shared test helpers: use await using
tmp = await tmpdir(...) to create the temp tree, write files into tmp.path
(replacing makeFixture behavior), and create the LSP instance with
provideTmpdirInstance(...) (or provideInstance(...) if no tmpdir-specific helper
exists) so the temp dir and instance are automatically cleaned up; locate
makeFixture and any Instance.provide(...) calls in this test and swap them to
use tmpdir + provideTmpdirInstance/provideInstance accordingly.

In `@packages/opencode/test/settings.test.ts`:
- Around line 6-27: Tests use AppRuntime.runPromise and mutate shared
Settings.Service state (setLspEnabled) without cleanup; switch to the standard
Effect test harness by declaring const it = testEffect(...) at the top and
convert each test to use it(...) with Effect.gen bodies, and ensure mutated
state is restored between tests (for example, call
Settings.Service.setLspEnabled(false) in a finally/ensuring block or use a
scoped Layer that provides a fresh Settings.Service per test) so
Settings.Service.lspEnabled and Settings.Service.setLspEnabled are exercised
with isolated state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 05d7b03d-8e0a-430c-afdd-2033ee660b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 787acf2 and 3653f0d.

📒 Files selected for processing (30)
  • .github/workflows/ci.yml
  • packages/app/src/app.tsx
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/global-sync.tsx
  • packages/app/src/context/settings.tsx
  • packages/app/src/i18n/en.ts
  • packages/app/src/i18n/zh.ts
  • packages/desktop-electron/src/main/env.d.ts
  • packages/desktop-electron/src/main/ipc.ts
  • packages/desktop-electron/src/preload/index.ts
  • packages/desktop-electron/src/preload/types.ts
  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/src/lsp/index.ts
  • packages/opencode/src/lsp/server.ts
  • packages/opencode/src/node.ts
  • packages/opencode/src/npm/index.ts
  • packages/opencode/src/project/instance.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/tool/registry.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: smoke-macos-arm64
  • GitHub Check: unit-windows-opencode-session
  • GitHub Check: unit-windows-opencode-server-tools
  • GitHub Check: unit-windows-opencode-config-project
  • GitHub Check: unit-windows-app
  • GitHub Check: unit-windows-desktop
  • GitHub Check: unit-opencode
  • GitHub Check: unit-desktop
  • GitHub Check: e2e-artifacts
  • GitHub Check: analyze-js-ts
🧰 Additional context used
📓 Path-based instructions (5)
packages/opencode/**/*.ts

📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)

packages/opencode/**/*.ts: Use Effect.gen(function* () { ... }) for Effect composition
Use Effect.fn("Domain.method") for named/traced effects and Effect.fnUntraced for internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer .pipe() wrappers
Use Effect.callback for callback-based APIs
Prefer DateTime.nowAsDate over new Date(yield* Clock.currentTimeMillis) when you need a Date in Effect code
Use Schema.Class for multi-field data in Effect schemas
Use branded schemas (Schema.brand) for single-value types in Effect
Use Schema.TaggedErrorClass for typed errors in Effect schemas
Use Schema.Defect instead of unknown for defect-like causes in Effect code
In Effect.gen / Effect.fn, prefer yield* new MyError(...) over yield* Effect.fail(new MyError(...)) for direct early-failure branches
Use makeRuntime from src/effect/run-service.ts for all services; it returns { runPromise, runFork, runCallback } backed by a shared memoMap that deduplicates layers
Use InstanceState from src/effect/instance-state.ts for per-directory or per-project state that needs per-instance cleanup; do work directly in the InstanceState.make closure where ScopedCache handles run-once semantics
Use Effect.addFinalizer or Effect.acquireRelease inside the InstanceState.make closure for cleanup (subscriptions, process teardown, etc.)
Use Effect.forkScoped inside the InstanceState.make closure for background stream consumers — the fiber is interrupted when the instance is disposed
Prefer FileSystem.FileSystem instead of raw fs/promises for effectful file I/O in Effect services
Prefer ChildProcessSpawner.ChildProcessSpawner with ChildProcess.make(...) instead of custom process wrappers in Effect services
Prefer HttpClient.HttpClient instead of raw fetch in Effect services
Prefer Path.Path, Config, Clock, and DateTime services when those concerns are already inside Effect code
For backgroun...

Files:

  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/src/project/instance.ts
  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/src/node.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/src/npm/index.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/src/lsp/index.ts
  • packages/opencode/src/lsp/server.ts
packages/desktop-electron/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)

Renderer process should only call window.api from src/preload

Files:

  • packages/desktop-electron/src/preload/types.ts
  • packages/desktop-electron/src/preload/index.ts
  • packages/desktop-electron/src/main/ipc.ts
  • packages/desktop-electron/src/main/env.d.ts
packages/app/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/app/AGENTS.md)

Always prefer createStore over multiple createSignal calls in SolidJS

Files:

  • packages/app/src/app.tsx
  • packages/app/src/context/global-sync.tsx
  • packages/app/src/i18n/en.ts
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/settings.tsx
  • packages/app/src/i18n/zh.ts
packages/opencode/test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (packages/opencode/test/AGENTS.md)

packages/opencode/test/**/*.test.{ts,tsx}: Use the tmpdir function from fixture/fixture.ts to create temporary directories for tests with automatic cleanup. Use await using syntax to ensure automatic cleanup when the variable goes out of scope.
When using the tmpdir function with git repository support, pass the git: true option to initialize a git repo with a root commit.
Use the config option in tmpdir to write an opencode.json config file during test setup by passing a partial Config.Info object.
Use the init option in tmpdir to define custom setup functions that can return extra data accessible via tmp.extra, and use the dispose option for custom cleanup logic.
Use testEffect(...) from test/lib/effect.ts for tests that exercise Effect services or Effect-based workflows.
Use it.effect(...) when the test should run with TestClock and TestConsole. Use it.live(...) when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.
Prefer Effect-aware helpers from fixture/fixture.ts over building manual runtimes in tests: use tmpdirScoped() for scoped temp directories, provideInstance(dir)(effect) for low-level binding without directory creation, provideTmpdirInstance(...) for single temp instance binding, or provideTmpdirServer(...) for tests that also need the test LLM server.
Define const it = testEffect(...) near the top of the test file and keep the test body inside Effect.gen(function* () { ... }). Yield services directly with yield* MyService.Service or yield* MyTool.
Avoid custom ManagedRuntime, attach(...), or ad hoc run(...) wrappers in Effect tests when testEffect(...) already provides the runtime.
When a test needs instance-local state, prefer provideTmpdirInstance(...) or provideInstance(...) over manual Instance.provide(...) inside Promise-style tests.

Files:

  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
packages/desktop-electron/src/main/ipc.ts

📄 CodeRabbit inference engine (packages/desktop-electron/AGENTS.md)

Main process should register IPC handlers in src/main/ipc.ts

Files:

  • packages/desktop-electron/src/main/ipc.ts
🧠 Learnings (51)
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `makeRuntime` from `src/effect/run-service.ts` for all services; it returns `{ runPromise, runFork, runCallback }` backed by a shared `memoMap` that deduplicates layers

Applied to files:

  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/src/lsp/index.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.

Applied to files:

  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
📚 Learning: 2026-04-22T09:32:54.556Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.

Applied to files:

  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.

Applied to files:

  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/desktop-electron/src/main/env.d.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
  • packages/opencode/src/tool/registry.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` services when those concerns are already inside Effect code

Applied to files:

  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/src/lsp/index.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.

Applied to files:

  • packages/opencode/src/effect/app-runtime.ts
  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.

Applied to files:

  • .github/workflows/ci.yml
  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/lsp/server.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics

Applied to files:

  • packages/opencode/src/project/instance.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/src/lsp/index.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When a test needs instance-local state, prefer `provideTmpdirInstance(...)` or `provideInstance(...)` over manual `Instance.provide(...)` inside Promise-style tests.

Applied to files:

  • packages/opencode/src/project/instance.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:08.774Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/desktop-electron/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:08.774Z
Learning: Applies to packages/desktop-electron/src/**/*.{ts,tsx,js,jsx} : Renderer process should only call `window.api` from `src/preload`

Applied to files:

  • packages/desktop-electron/src/preload/types.ts
  • packages/desktop-electron/src/preload/index.ts
📚 Learning: 2026-04-23T07:23:23.849Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 180
File: packages/app/src/components/session/session-new-view.tsx:13-18
Timestamp: 2026-04-23T07:23:23.849Z
Learning: In pawwork (Astro-Han/pawwork), prefer using `createStore` instead of multiple `createSignal` calls only when the signals represent **coupled** object state that is updated together (i.e., there is at least one shared batch-update site where the state is changed in the same transaction). If the state fields are **independent** and are mutated by separate handlers (e.g., one handler updates only `selectedSkill` while another updates only `mode`), keep them as individual `createSignal` calls—using `createStore` for truly independent fields adds boilerplate without behavioral benefit.

Applied to files:

  • packages/app/src/app.tsx
  • packages/app/src/context/global-sync.tsx
  • packages/app/src/i18n/en.ts
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/settings.tsx
  • packages/app/src/i18n/zh.ts
📚 Learning: 2026-04-23T15:10:21.635Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 191
File: packages/app/src/components/session/pawwork-skill-meta.ts:38-39
Timestamp: 2026-04-23T15:10:21.635Z
Learning: This repo configures Tailwind v4 with `--color-*: initial`, which effectively breaks standard Tailwind palette utilities (e.g., `text-violet-500` can resolve to no CSS variable and render as a no-op/black). For brand/accent colors that are not backed by semantic design tokens, use inline styles with the exact hex value (e.g., `style={{ color: '#8B5FBF' }}` / `homeIconStyle: { color: '#8B5FBF' }`) and add a short comment explaining that Tailwind palette utilities won’t work due to the `--color-*: initial` setup. Do not suggest replacing these inline hex colors with Tailwind palette classes anywhere in this repo.

Applied to files:

  • packages/app/src/app.tsx
  • packages/app/src/context/global-sync.tsx
  • packages/app/src/i18n/en.ts
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/context/settings.tsx
  • packages/app/src/i18n/zh.ts
📚 Learning: 2026-04-25T09:19:30.734Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 231
File: packages/desktop-electron/src/main/index.ts:537-537
Timestamp: 2026-04-25T09:19:30.734Z
Learning: In Astro-Han/pawwork (`packages/desktop-electron/src/main/`), the convention for IPC registration is that `index.ts` (the bootstrap entry) directly calls each registration function. `registerIpcHandlers({...})` in `src/main/ipc.ts` is a single fat options-bag with inline handler bodies. New, cohesive IPC features (e.g., About) are placed in sub-modules like `src/main/ipc/about.ts`, which own their own exported types, helpers, and a `register*Ipc()` function. The bootstrap in `index.ts` calls each `register*Ipc()` directly — this is intentional, not fragmentation. Do NOT suggest routing sub-module IPC registrations through `ipc.ts`.

Applied to files:

  • packages/desktop-electron/src/preload/index.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : When using the `tmpdir` function with git repository support, pass the `git: true` option to initialize a git repo with a root commit.

Applied to files:

  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/lsp/server.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `init` option in `tmpdir` to define custom setup functions that can return extra data accessible via `tmp.extra`, and use the `dispose` option for custom cleanup logic.

Applied to files:

  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `tmpdir` function from `fixture/fixture.ts` to create temporary directories for tests with automatic cleanup. Use `await using` syntax to ensure automatic cleanup when the variable goes out of scope.

Applied to files:

  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.

Applied to files:

  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
📚 Learning: 2026-04-23T08:51:00.819Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 186
File: packages/opencode/test/plugin/workspace-adaptor.test.ts:139-144
Timestamp: 2026-04-23T08:51:00.819Z
Learning: For pawwork tests under packages/opencode/test/**, auth.json teardown may intentionally combine `Filesystem.write` (from `packages/opencode/src/util/filesystem.ts`) with `node:fs/promises` `unlink` for cleanup. Do not flag this as inconsistent style; it is the established/intentional pattern because `Filesystem` does not provide a `remove`/`unlink` helper.

Applied to files:

  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Prefer the `project` fixture for tests that need a dedicated project with LLM mocking

Applied to files:

  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:21:56.373Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 71
File: packages/app/src/components/session/session-status-connections.tsx:146-147
Timestamp: 2026-04-20T14:21:56.373Z
Learning: In the Astro-Han/pawwork repository (SolidJS app), `sync.data.config` is always initialized to `{}` at `packages/app/src/context/global-sync.tsx` line 71 and is never `undefined` at runtime. Non-optional property access like `sync.data.config.plugin` is intentional and consistent with the pattern used in `packages/app/src/components/status-popover-body.tsx` line 243. Do not flag `sync.data.config.plugin` as needing optional chaining.

Applied to files:

  • packages/app/src/context/global-sync.tsx
📚 Learning: 2026-04-20T17:03:40.214Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 73
File: packages/opencode/src/cli/cmd/tui/context/sync.tsx:486-489
Timestamp: 2026-04-20T17:03:40.214Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/cli/cmd/tui/context/sync.tsx`), `sync.ready` returning `true` when `process.env.OPENCODE_FAST_BOOT` is set is intentional. The plugin-facing data properties `state.config` (initialized to `{}`) and `state.provider` (initialized to `[]`) expose safe-empty defaults, so they are safe to access before bootstrap completes. Do not flag these as needing null-guards or conditional patterns to match `vcs` — the difference is intentional because `vcs` starts as `undefined` while the others have initialized defaults. Changing this would alter the plugin API contract without a concrete failing case.

Applied to files:

  • packages/app/src/context/global-sync.tsx
📚 Learning: 2026-04-24T17:12:23.931Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/desktop-electron/electron-builder.config.ts:14-18
Timestamp: 2026-04-24T17:12:23.931Z
Learning: In Astro-Han/pawwork, the `localizedMacDisplayNameByChannel` map in `packages/desktop-electron/electron-builder.config.ts` is intentionally kept separate from `localizedAppDisplayName` in `packages/desktop-electron/src/main/app-display-name.ts`. The former is a build-time packaging helper; the latter is a runtime UI helper that localizes the current app name by locale. Coupling them would introduce a build-time dependency on runtime main logic. Do not suggest deduplicating or sharing this mapping — the explicit local table is covered by focused regression tests in `electron-builder-app-update.test.ts`.

Applied to files:

  • packages/app/src/context/global-sync.tsx
  • packages/desktop-electron/src/main/env.d.ts
  • packages/app/src/i18n/zh.ts
📚 Learning: 2026-04-22T05:32:29.012Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 98
File: packages/desktop-electron/src/main/menu-labels.ts:1-2
Timestamp: 2026-04-22T05:32:29.012Z
Learning: In Astro-Han/pawwork, the app i18n layer (`packages/app/src/i18n/`) only contains `en.ts` and `zh.ts`, and `normalizeLocale` (in `packages/app/src/context/language.tsx`) only returns `"en"` or `"zh"`. The desktop `MenuLocale = "en" | "zh"` union in `packages/desktop-electron/src/main/menu-labels.ts` is intentionally limited to these two locales and is not a broader restriction — do not flag it as overly restrictive or suggest adding other locales.

Applied to files:

  • packages/app/src/context/global-sync.tsx
  • packages/app/src/i18n/en.ts
  • packages/desktop-electron/src/main/env.d.ts
  • packages/app/src/i18n/zh.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.

Applied to files:

  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/test/tool/registry.test.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/src/lsp/index.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/packages/app/src/testing/**/*.ts : Test-only hooks must be inert unless explicitly enabled and should not add normal-runtime listeners, reactive subscriptions, or per-update allocations

Applied to files:

  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `it.effect(...)` when the test should run with `TestClock` and `TestConsole`. Use `it.live(...)` when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.

Applied to files:

  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Import test utilities from `../fixtures` instead of `playwright/test`

Applied to files:

  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/lsp/server.test.ts
  • packages/opencode/test/lsp/index.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.fn("Domain.method")` for named/traced effects and `Effect.fnUntraced` for internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer `.pipe()` wrappers

Applied to files:

  • packages/opencode/test/tool/read.test.ts
  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/settings/index.ts
📚 Learning: 2026-04-24T17:08:44.294Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:44.294Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.

Applied to files:

  • packages/app/src/i18n/en.ts
  • packages/app/src/i18n/zh.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use fixture-managed cleanup with `withSession(sdk, title, callback)` for temporary sessions instead of calling `sdk.session.delete(...)` directly

Applied to files:

  • packages/opencode/test/session/snapshot-tool-race.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.addFinalizer` or `Effect.acquireRelease` inside the `InstanceState.make` closure for cleanup (subscriptions, process teardown, etc.)

Applied to files:

  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/src/lsp/index.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation rather than storing `Fiber | undefined` or `Promise | undefined` manually

Applied to files:

  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/src/tool/read.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.gen(function* () { ... })` for Effect composition

Applied to files:

  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/settings/index.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.callback` for callback-based APIs

Applied to files:

  • packages/opencode/test/session/snapshot-tool-race.test.ts
  • packages/opencode/test/session/prompt-effect.test.ts
📚 Learning: 2026-04-24T03:51:54.050Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 206
File: packages/app/e2e/prompt/prompt-footer-focus.spec.ts:131-143
Timestamp: 2026-04-24T03:51:54.050Z
Learning: In Astro-Han/pawwork E2E tests (packages/app/e2e/fixtures.ts), `project.prompt(text)` internally calls `trackSession(next.sessionID, active.directory)` after the prompt submission is observed and the active session is resolved. Tests that obtain a `sessionID` from `project.prompt()` do NOT need to call `project.trackSession(sessionID)` manually — it is already registered for teardown. Calling it again would be duplicate ownership.

Applied to files:

  • packages/opencode/test/session/prompt-effect.test.ts
📚 Learning: 2026-04-21T12:14:30.524Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 93
File: packages/opencode/test/server/cors-middleware.test.ts:15-23
Timestamp: 2026-04-21T12:14:30.524Z
Learning: In `packages/opencode/test/server/cors-middleware.test.ts` (and similar Hono-based test files), `app.request(...)` on the Hono `Hono` app type returns `Promise<Response> | Response` — a union that includes a non-PromiseLike branch. `Effect.promise(...)` requires a `PromiseLike`, so wrapping with `Promise.resolve(app.request(...))` is necessary to normalize the union type. Removing the wrapper causes a TS2322 typecheck error in `packages/opencode`.

Applied to files:

  • packages/opencode/test/session/prompt-effect.test.ts
📚 Learning: 2026-04-23T15:25:31.118Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 193
File: packages/app/e2e/sidebar/sidebar-leading-slot.spec.ts:5-55
Timestamp: 2026-04-23T15:25:31.118Z
Learning: In Astro-Han/pawwork E2E tests, reaching a real "running" session state requires the `project` fixture (for model bootstrap) plus `llm.wait(1)` orchestration. The bare `sdk` fixture used by `withSession` does not trigger an LLM call even with `agent: "build"` + `system` prompt set via `sdk.session.promptAsync`, so simulating running state is not lightweight in the current test infrastructure.

Applied to files:

  • packages/opencode/test/session/prompt-effect.test.ts
📚 Learning: 2026-04-25T09:19:30.734Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 231
File: packages/desktop-electron/src/main/index.ts:537-537
Timestamp: 2026-04-25T09:19:30.734Z
Learning: In Astro-Han/pawwork (packages/desktop-electron/src/main/), follow the IPC registration convention: the bootstrap entry (packages/desktop-electron/src/main/index.ts) should directly call each module’s exported register*Ipc() function. Do not route/centralize these sub-module IPC registrations through src/main/ipc.ts. Keep sub-module IPC features cohesive (e.g., src/main/ipc/about.ts should own its types/helpers and expose register*Ipc()), and allow index.ts to aggregate by calling each register*Ipc() directly.

Applied to files:

  • packages/desktop-electron/src/main/ipc.ts
  • packages/desktop-electron/src/main/env.d.ts
📚 Learning: 2026-04-25T12:52:32.462Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 234
File: packages/desktop-electron/src/main/ipc.ts:238-263
Timestamp: 2026-04-25T12:52:32.462Z
Learning: In Astro-Han/pawwork (`packages/desktop-electron/src/main/ipc.ts`), `deps.getServerReadyData()` (backed by `serverReady.promise` in `index.ts`) resolves once at server startup and remains settled; it is not expected to reject in practice. Do not flag the absence of a try-catch around it in the `export-session` IPC handler — the network/fetch layer in `server-client.ts` already has a 10-second AbortController timeout and returns a typed `{ok: false, error}` payload, covering the real failure modes.

Applied to files:

  • packages/desktop-electron/src/main/ipc.ts
  • packages/opencode/src/npm/index.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Test one feature per test file

Applied to files:

  • packages/opencode/test/settings.test.ts
  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O in Effect services

Applied to files:

  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/src/lsp/server.ts
📚 Learning: 2026-04-25T12:52:35.671Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 234
File: packages/opencode/src/session/export.ts:24-31
Timestamp: 2026-04-25T12:52:35.671Z
Learning: In `packages/opencode/src/session/export.ts`, the `hashFile` async helper intentionally uses raw `node:fs/promises` (`fs.readFile`) rather than `FileSystem.FileSystem`. It is a small, self-contained helper called inside `Effect.promise(...)` with broad failure tolerance (try/catch returning `undefined`). Using `FileSystem.FileSystem` here would add unnecessary layers without payoff. The `FileSystem.FileSystem` preference applies to Effect-service-style operations, not thin async boundary helpers like `hashFile`.

Applied to files:

  • packages/opencode/src/tool/read.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `ChildProcessSpawner.ChildProcessSpawner` with `ChildProcess.make(...)` instead of custom process wrappers in Effect services

Applied to files:

  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/lsp/index.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : For background loops or scheduled tasks, use `Effect.repeat` or `Effect.schedule` with `Effect.forkScoped` in the layer definition

Applied to files:

  • packages/opencode/src/tool/read.ts
  • packages/opencode/src/settings/index.ts
  • packages/opencode/src/lsp/index.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches

Applied to files:

  • packages/opencode/src/tool/read.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : When validating routing, assert against canonical or resolved workspace slugs using shared helpers from `../actions` to account for Windows canonicalization

Applied to files:

  • packages/opencode/test/lsp/server.test.ts
📚 Learning: 2026-04-25T12:52:47.074Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 234
File: packages/opencode/src/share/session.ts:27-27
Timestamp: 2026-04-25T12:52:47.074Z
Learning: In `packages/opencode/src/share/session.ts`, the local `ensureEnabled` closure is intentionally duplicated from `ShareRuntime.ensureEnabled` in `runtime.ts`. Using the shared `ShareRuntime.ensureEnabled` effect directly would leak `CloudShareGate` back into the `R` (requirement) type of `share`, `unshare`, and `create`, because the shared effect re-yields the service rather than capturing an already-resolved gate reference. The current pattern resolves `gate` once at the top of the outer `Effect.gen` and then closes over it in a local `ensureEnabled`, keeping the inner effects' requirement types clean. A comment in the file points to `runtime.ts` to make the intentional duplication discoverable. The real fix would be refactoring `ensureEnabled` to accept a gate parameter, but that change is larger than the DRY benefit warrants. Do not flag this duplication as a maintenance issue.

Applied to files:

  • packages/opencode/src/settings/index.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use lowercase, descriptive test names (e.g., 'sidebar can be toggled')

Applied to files:

  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Prefer fluent helpers and drivers when they make intent obvious and reduce locator-heavy noise in tests

Applied to files:

  • packages/opencode/test/lsp/index.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : In terminal tests, type through the browser using `runTerminal()` and `waitTerminalReady()` instead of writing to the PTY through the SDK

Applied to files:

  • packages/opencode/test/lsp/index.test.ts
  • packages/opencode/test/lsp/lspEnabled.test.ts
  • packages/opencode/test/lsp/lifecycle.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/**/*.spec.ts : Use `expect.poll(...)` for probing mock or backend state rather than transient DOM visibility

Applied to files:

  • packages/opencode/test/lsp/lifecycle.test.ts
🔇 Additional comments (22)
packages/opencode/src/project/instance.ts (1)

78-80: Good addition: explicit cached-instance directory accessor.

Exposing a snapshot of cached directories here is a clean way to support lifecycle coordination (like invalidate/shutdown flows) without leaking cache directly.

packages/opencode/src/settings/index.ts (1)

12-29: Settings service wiring looks correct and coherent.

Ref<boolean>(false) + service accessors + makeRuntime wrappers cleanly implement the default-off feature flag with a simple integration surface.

.github/workflows/ci.yml (1)

399-399: Nice CI shard update.

Including test/settings.test.ts in this shard keeps Windows advisory coverage aligned with the new settings behavior tests.

packages/opencode/src/effect/app-runtime.ts (1)

36-36: App runtime layer integration is correct.

Adding Settings.defaultLayer into AppLayer is the right runtime wiring step for feature-flagged LSP behavior.

Also applies to: 59-59

packages/opencode/test/github/ci-workflow.test.ts (1)

57-57: Workflow contract test stays in sync.

Good update to the pinned shard command so the guard test reflects the CI workflow change exactly.

packages/opencode/test/lsp/index.test.ts (1)

10-15: Per-test LSP flag setup/teardown is a solid test isolation change.

Enabling in beforeEach and resetting in afterEach makes these spawn assertions deterministic under the new default-off setting.

packages/app/src/app.tsx (1)

85-85: Type surface update is appropriate.

The optional setLspEnabled bridge typing cleanly matches renderer usage while remaining safe for environments where window.api is absent.

packages/opencode/test/tool/read.test.ts (1)

578-588: Useful regression guard for read-path LSP warming removal.

This test directly protects the intended behavior change by preventing accidental reintroduction of touchFile/warm-hook logic in read.ts.

packages/opencode/test/session/snapshot-tool-race.test.ts (1)

44-44: Good test harness alignment with new LSP/Settings requirements.

The added Settings layer and the shutdownAll / invalidate mock methods keep this test’s dependency graph and LSP mock contract consistent with the updated runtime behavior.

Also applies to: 102-103, 132-132

packages/desktop-electron/src/preload/types.ts (1)

97-97: API typing update is clean and consistent.

Adding setLspEnabled to ElectronAPI keeps the preload contract explicit for the new IPC pathway.

packages/app/src/context/global-sync.tsx (1)

344-354: Good addition of user-visible LSP install failure feedback.

This event handler cleanly maps the new bus event into an error toast with localized title/description.

packages/desktop-electron/src/preload/index.ts (1)

83-83: Preload IPC bridge wiring looks correct.

The new setLspEnabled method follows the established preload pattern and keeps renderer/main separation intact.

packages/opencode/src/tool/read.ts (1)

2-2: Import cleanup is aligned with the read-path behavior change.

This keeps the module surface focused after removing LSP warming from the read flow.

packages/opencode/src/node.ts (1)

7-10: Public export surface update is appropriate.

These re-exports expose the new runtime control points needed by the desktop integration layer.

packages/app/src/components/settings-general.tsx (1)

302-312: LSP toggle row integration looks consistent and correct.

The new row follows the existing settings row pattern and correctly wires checked/onChange to settings.general.

packages/opencode/test/session/prompt-effect.test.ts (2)

150-151: LSP test double now correctly mirrors lifecycle APIs.

Adding shutdownAll and invalidate keeps the mock aligned with the updated LSP.Service contract and prevents interface drift in this suite.


177-180: Good dependency wiring for ToolRegistry test runtime.

Providing Settings.defaultLayer here is the right fix after gating lsp tool visibility through settings.

packages/opencode/test/tool/registry.test.ts (1)

501-553: Coverage for LSP-gated registry behavior is solid.

These cases validate both static toggles and post-invalidate() recomputation, which directly protects the new runtime toggle path.

packages/opencode/test/lsp/lifecycle.test.ts (1)

26-34: Lifecycle setup now correctly controls LSP enablement per test.

Enabling in beforeEach and restoring in afterEach makes these tests deterministic against the new settings gate.

packages/opencode/src/lsp/server.ts (2)

35-43: Root marker consolidation is a strong improvement.

Using JavascriptPackageRoot() across TS/Vue/ESLint/Svelte/Astro/YamlLS makes root resolution behavior consistent and directly addresses monorepo over-expansion.

Also applies to: 109-109, 138-138, 166-166, 1020-1020, 1048-1048, 1307-1307


1978-1982: LSP_SERVER_PACKAGES as a derived set is a good single source of truth.

This centralization reduces drift between server definitions and npm-install failure handling paths.

packages/opencode/src/npm/index.ts (1)

31-45: Install-failure signaling is well integrated.

Emitting lsp.server.install.failed only for known LSP server packages, then rethrowing InstallFailedError, cleanly supports retryable failures plus user-facing visibility.

Also applies to: 118-132

Comment thread packages/app/src/context/settings.tsx Outdated
Comment thread packages/app/src/i18n/en.ts Outdated
Comment thread packages/app/src/i18n/zh.ts Outdated
Comment thread packages/desktop-electron/src/main/ipc.ts Outdated
Comment thread packages/opencode/src/lsp/index.ts
Comment thread packages/opencode/src/lsp/index.ts
Comment thread packages/opencode/test/lsp/lspEnabled.test.ts Outdated
Comment thread packages/opencode/test/lsp/lspEnabled.test.ts Outdated
Comment thread packages/opencode/test/lsp/server.test.ts Outdated
Comment thread packages/opencode/test/settings.test.ts

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review pass: three concrete inline findings, graded by P-level.

Comment thread packages/app/src/context/settings.tsx
Comment thread packages/opencode/src/lsp/server.ts Outdated
Comment thread packages/opencode/src/lsp/index.ts
Repo i18n strings interpolate via {{var}}; the new toast description
was using single braces, so {pkg} and {error} would render literally
instead of being replaced with the failed package and error message.

Refs #232
…PC failure

Replace fire-and-forget IPC in setLspEnabled with a createEffect that
runs after the persisted store rehydrates. The effect sends the current
value to the Electron main process whenever it changes (covering both
the startup case where the renderer loaded a persisted true while the
main-process Ref defaulted to false, and the runtime toggle case). On
IPC rejection it flips the store back so the UI reflects the actual
runtime state instead of drifting silently.

Refs #232
Switch the per-Instance loop to Promise.allSettled so a failure on one
project does not leave subsequent projects with stale LSP/ToolRegistry
caches. Rejected results are logged with the directory but do not abort
the toggle for the remaining instances.

Refs #232
Track per-server-key timestamps in a new s.installCooldownUntil map.
After a Npm.InstallFailedError the entry stays out of s.broken (so the
user can recover once the network or registry is back) but is skipped
for INSTALL_RETRY_COOLDOWN_MS (60s). This prevents every subsequent
touchFile/hover/definition from re-triggering an arborist install and
spamming lsp.server.install.failed toasts on offline machines.

Refs #232
Convert test/settings.test.ts, test/lsp/server.test.ts, and
test/lsp/lspEnabled.test.ts to testEffect(...) + provideTmpdirInstance
per packages/opencode/test/AGENTS.md. Spy-based gates keep bun:test
spyOn since it composes with Effect just fine. Each test that mutates
Settings.lspEnabled now resets it to false in finally so later tests
do not inherit the toggle through the singleton memoMap.

Also widen the install-failure contract assertion to match the new
cooldown branch shape (s.installCooldownUntil + s.broken.add(key)).

Refs #232
Typescript and Astro language servers were always resolving
typescript/lib/tsserver.js from Instance.directory, even though the
NearestRoot fix already narrowed the spawn cwd to the nearest package.
In a monorepo where packages/app installs its own typescript, the
tsserver SDK could mismatch the package's version. Try the resolved
package root first and fall back to the worktree root only when the
package does not have its own typescript install (covers hoisted
monorepos).

Refs #232
The renderer caches LSP status and only refreshes on lsp.updated. After
LSP.shutdownAll kills clients, the cached status would otherwise keep
showing the previous connected servers in the popover until the next
unrelated event. Publish Updated when shutdown actually had clients
to drop, so the toggle-off path stays visually consistent with the
runtime state.

Refs #232
@Astro-Han Astro-Han merged commit 47faed6 into dev Apr 26, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request harness Model harness, prompts, tool descriptions, and session mechanics P1 High priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] TypeScript LSP can consume excessive CPU and memory on large workspaces

1 participant