[Bug]: senpi LSP user config (~/.pi/lsp-client.json) is migrated away by the brand-dir migration, silently disabling custom LSP servers
Prerequisites
- I will write this issue in English
- I have searched existing issues to avoid duplicates — related: #8039 (same migration, agent dir), #6327 and #6486 (same LSP config/timeout area)
- I am using the latest version: reproduced on omo-ai 5.0.0-0.beta.63; the hardcoded path is still present in 5.0.0-0.beta.65
- I have read the documentation and could not find the answer
Environment
- macOS 15 (Darwin 25.6.0), arm64 (Apple M2)
- omo-ai 5.0.0-0.beta.63, @code-yeongyu/senpi 2026.9.15-2
- bash-language-server 5.6.0 (Homebrew), node v24.18.0
Summary
Two components disagree about where the senpi LSP user config lives: the LSP adapter reads ~/.pi/lsp-client.json (hardcoded), while the engine's migrateLegacySenpiDirs moves ~/.pi/* into ~/.omo/*. A user's custom LSP server definitions are therefore relocated to a path the reader never consults, and they silently stop applying. Builtins keep working, which hides the failure. Same migration as #8039, different (and currently unreported) consequence.
Steps to reproduce
- Put a custom (non-builtin) server in
~/.pi/lsp-client.json:{"lsp": {"probe-custom": {"command": ["/nonexistent/probe-binary"], "extensions": [".zztest"]}}} - Request diagnostics for a
.zztestfile (senpilsp_diagnostics). The config is read — the server is resolved and the spawn is attempted:LSP server 'probe-custom' for .zztest is NOT INSTALLED. Command not found: /nonexistent/probe-binary - Launch omo/senpi with cwd =
$HOME. It prints:Migrated missing project config directory entries /Users/<user>/.pi → /Users/<user>/.omoand~/.piis left empty. - Repeat the same diagnostics request →
No LSP server configured for extension: .zztest. The config is no longer read. - Placing the same file at
~/.omo/lsp-client.json(the branded dir) does not restore it — the senpi LSP adapter never looks there.
Actual behaviour
~/.omo/lsp-client.jsonis ignored by the senpi LSP adapter (step 5).~/.pi/lsp-client.jsonis read, but the migration empties~/.pion every launch from$HOME. The file only survives by accident if the destination already holds a same-named entry (the migration skips entries whose target exists).
Expected behaviour
One of:
- the senpi LSP adapter resolves the user/project config under
CONFIG_DIR_NAME(.omo) with.pias a read-only fallback, or - the migration does not move files the branded layout does not own (e.g. skip
lsp-client.json), or copies instead of moves — mirroringbrand-dir-migration.ts's deliberate "COPIED once — never moved" treatment of~/.senpi.
Root cause (code)
- Reader:
omo-ai/plugin/extensions/omo.js(bundled) builds the per-call context withprojectConfigPaths:[join(cwd, ".pi", "lsp-client.json")]anduserConfigPath: join(HOME, ".pi", "lsp-client.json"); upstreampackages/omo-senpi/src/components/lsp/AGENTS.mddocuments the same. - Mover:
@code-yeongyu/senpi/dist/legacy-senpi-dir-migration.js→migrateLegacySenpiDirs(cwd). Withcwd = $HOMEit renames entries from~/.piinto~/.omo, printing the warning only when something actually moved.renameSyncpreserves mtime, so the relocated file keeps its old timestamp.
Secondary findings (same area)
- A custom
commandthat omits the server's subcommand args blocks for the full timeout. With"command": ["/path/bash-lsp"](missingstart) the call ends withLSP daemon request timed out after 30000ms, whiledaemon.logonly recordsLSP server bash-node24 ... exited with code 0. Adding"start"resolved it. A fast, actionable error (ideally echoing the spawned argv) would prevent this — related to #6486. - The guidance message prints the same path twice:
Configure a custom server in '/Users/<user>/.pi/lsp-client.json' or '/Users/<user>/.pi/lsp-client.json'. - Documented paths disagree: the lsp-setup skill points at
~/.codex/lsp-client.json, the OpenCode/omo translator uses<cwd>/.opencode/lsp.json/<cwd>/.omo/lsp.json/<cwd>/.omo/lsp-client.json, and the senpi adapter uses~/.pi/lsp-client.json. For omo users the branded~/.omo/lsp-client.jsonis a natural-looking but dead location. - Not a bug, but easy to misread: for a builtin id, the user config's
commandis ignored. Verified:{"bash": {"command": ["/path/bash-lsp"], ...}}still spawnedbash-language-server start. Docs state this for project configs and for fully custom servers; the senpi user-config case is not spelled out (#6327 covers the same gap for OpenCode).
Impact
Custom LSP servers and command overrides (e.g. pinning a node keg, adding --query-driver) silently stop applying after the first launch from $HOME. No error surfaces and builtins mask it, so a carefully configured LSP can be lost without the user noticing.
Source: code-yeongyu/oh-my-openagent