@oh-my-pi/pi-coding-agent 3.25.0 → 3.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +90 -0
- package/package.json +5 -5
- package/src/cli/args.ts +4 -0
- package/src/core/agent-session.ts +29 -2
- package/src/core/bash-executor.ts +2 -1
- package/src/core/custom-commands/bundled/review/index.ts +369 -14
- package/src/core/custom-commands/bundled/wt/index.ts +1 -1
- package/src/core/session-manager.ts +158 -246
- package/src/core/session-storage.ts +379 -0
- package/src/core/settings-manager.ts +155 -4
- package/src/core/system-prompt.ts +62 -64
- package/src/core/tools/ask.ts +5 -4
- package/src/core/tools/bash-interceptor.ts +26 -61
- package/src/core/tools/bash.ts +13 -8
- package/src/core/tools/complete.ts +2 -4
- package/src/core/tools/edit-diff.ts +11 -4
- package/src/core/tools/edit.ts +7 -13
- package/src/core/tools/find.ts +111 -50
- package/src/core/tools/gemini-image.ts +128 -147
- package/src/core/tools/grep.ts +397 -415
- package/src/core/tools/index.test.ts +5 -1
- package/src/core/tools/index.ts +6 -8
- package/src/core/tools/jtd-to-json-schema.ts +174 -196
- package/src/core/tools/ls.ts +12 -10
- package/src/core/tools/lsp/client.ts +58 -9
- package/src/core/tools/lsp/config.ts +205 -656
- package/src/core/tools/lsp/defaults.json +465 -0
- package/src/core/tools/lsp/index.ts +55 -32
- package/src/core/tools/lsp/rust-analyzer.ts +49 -10
- package/src/core/tools/lsp/types.ts +1 -0
- package/src/core/tools/lsp/utils.ts +1 -1
- package/src/core/tools/read.ts +152 -76
- package/src/core/tools/render-utils.ts +70 -10
- package/src/core/tools/review.ts +38 -126
- package/src/core/tools/task/artifacts.ts +5 -4
- package/src/core/tools/task/executor.ts +204 -67
- package/src/core/tools/task/index.ts +129 -92
- package/src/core/tools/task/name-generator.ts +1544 -214
- package/src/core/tools/task/parallel.ts +30 -3
- package/src/core/tools/task/render.ts +85 -39
- package/src/core/tools/task/types.ts +34 -11
- package/src/core/tools/task/worker.ts +152 -27
- package/src/core/tools/web-fetch.ts +220 -1657
- package/src/core/tools/web-scrapers/academic.test.ts +239 -0
- package/src/core/tools/web-scrapers/artifacthub.ts +215 -0
- package/src/core/tools/web-scrapers/arxiv.ts +88 -0
- package/src/core/tools/web-scrapers/aur.ts +175 -0
- package/src/core/tools/web-scrapers/biorxiv.ts +141 -0
- package/src/core/tools/web-scrapers/bluesky.ts +284 -0
- package/src/core/tools/web-scrapers/brew.ts +177 -0
- package/src/core/tools/web-scrapers/business.test.ts +82 -0
- package/src/core/tools/web-scrapers/cheatsh.ts +78 -0
- package/src/core/tools/web-scrapers/chocolatey.ts +158 -0
- package/src/core/tools/web-scrapers/choosealicense.ts +110 -0
- package/src/core/tools/web-scrapers/cisa-kev.ts +100 -0
- package/src/core/tools/web-scrapers/clojars.ts +180 -0
- package/src/core/tools/web-scrapers/coingecko.ts +184 -0
- package/src/core/tools/web-scrapers/crates-io.ts +128 -0
- package/src/core/tools/web-scrapers/crossref.ts +149 -0
- package/src/core/tools/web-scrapers/dev-platforms.test.ts +254 -0
- package/src/core/tools/web-scrapers/devto.ts +177 -0
- package/src/core/tools/web-scrapers/discogs.ts +308 -0
- package/src/core/tools/web-scrapers/discourse.ts +221 -0
- package/src/core/tools/web-scrapers/dockerhub.ts +160 -0
- package/src/core/tools/web-scrapers/documentation.test.ts +85 -0
- package/src/core/tools/web-scrapers/fdroid.ts +158 -0
- package/src/core/tools/web-scrapers/finance-media.test.ts +144 -0
- package/src/core/tools/web-scrapers/firefox-addons.ts +214 -0
- package/src/core/tools/web-scrapers/flathub.ts +239 -0
- package/src/core/tools/web-scrapers/git-hosting.test.ts +272 -0
- package/src/core/tools/web-scrapers/github-gist.ts +68 -0
- package/src/core/tools/web-scrapers/github.ts +455 -0
- package/src/core/tools/web-scrapers/gitlab.ts +456 -0
- package/src/core/tools/web-scrapers/go-pkg.ts +275 -0
- package/src/core/tools/web-scrapers/hackage.ts +94 -0
- package/src/core/tools/web-scrapers/hackernews.ts +208 -0
- package/src/core/tools/web-scrapers/hex.ts +121 -0
- package/src/core/tools/web-scrapers/huggingface.ts +385 -0
- package/src/core/tools/web-scrapers/iacr.ts +86 -0
- package/src/core/tools/web-scrapers/index.ts +250 -0
- package/src/core/tools/web-scrapers/jetbrains-marketplace.ts +169 -0
- package/src/core/tools/web-scrapers/lemmy.ts +220 -0
- package/src/core/tools/web-scrapers/lobsters.ts +186 -0
- package/src/core/tools/web-scrapers/mastodon.ts +310 -0
- package/src/core/tools/web-scrapers/maven.ts +152 -0
- package/src/core/tools/web-scrapers/mdn.ts +174 -0
- package/src/core/tools/web-scrapers/media.test.ts +138 -0
- package/src/core/tools/web-scrapers/metacpan.ts +253 -0
- package/src/core/tools/web-scrapers/musicbrainz.ts +273 -0
- package/src/core/tools/web-scrapers/npm.ts +114 -0
- package/src/core/tools/web-scrapers/nuget.ts +205 -0
- package/src/core/tools/web-scrapers/nvd.ts +243 -0
- package/src/core/tools/web-scrapers/ollama.ts +267 -0
- package/src/core/tools/web-scrapers/open-vsx.ts +119 -0
- package/src/core/tools/web-scrapers/opencorporates.ts +275 -0
- package/src/core/tools/web-scrapers/openlibrary.ts +319 -0
- package/src/core/tools/web-scrapers/orcid.ts +299 -0
- package/src/core/tools/web-scrapers/osv.ts +189 -0
- package/src/core/tools/web-scrapers/package-managers-2.test.ts +199 -0
- package/src/core/tools/web-scrapers/package-managers.test.ts +171 -0
- package/src/core/tools/web-scrapers/package-registries.test.ts +259 -0
- package/src/core/tools/web-scrapers/packagist.ts +174 -0
- package/src/core/tools/web-scrapers/pub-dev.ts +185 -0
- package/src/core/tools/web-scrapers/pubmed.ts +178 -0
- package/src/core/tools/web-scrapers/pypi.ts +129 -0
- package/src/core/tools/web-scrapers/rawg.ts +124 -0
- package/src/core/tools/web-scrapers/readthedocs.ts +126 -0
- package/src/core/tools/web-scrapers/reddit.ts +104 -0
- package/src/core/tools/web-scrapers/repology.ts +262 -0
- package/src/core/tools/web-scrapers/research.test.ts +107 -0
- package/src/core/tools/web-scrapers/rfc.ts +209 -0
- package/src/core/tools/web-scrapers/rubygems.ts +117 -0
- package/src/core/tools/web-scrapers/searchcode.ts +217 -0
- package/src/core/tools/web-scrapers/sec-edgar.ts +274 -0
- package/src/core/tools/web-scrapers/security.test.ts +103 -0
- package/src/core/tools/web-scrapers/semantic-scholar.ts +190 -0
- package/src/core/tools/web-scrapers/snapcraft.ts +200 -0
- package/src/core/tools/web-scrapers/social-extended.test.ts +192 -0
- package/src/core/tools/web-scrapers/social.test.ts +259 -0
- package/src/core/tools/web-scrapers/sourcegraph.ts +373 -0
- package/src/core/tools/web-scrapers/spdx.ts +121 -0
- package/src/core/tools/web-scrapers/spotify.ts +218 -0
- package/src/core/tools/web-scrapers/stackexchange.test.ts +120 -0
- package/src/core/tools/web-scrapers/stackoverflow.ts +124 -0
- package/src/core/tools/web-scrapers/standards.test.ts +122 -0
- package/src/core/tools/web-scrapers/terraform.ts +304 -0
- package/src/core/tools/web-scrapers/tldr.ts +51 -0
- package/src/core/tools/web-scrapers/twitter.ts +96 -0
- package/src/core/tools/web-scrapers/types.ts +234 -0
- package/src/core/tools/web-scrapers/utils.ts +162 -0
- package/src/core/tools/web-scrapers/vimeo.ts +152 -0
- package/src/core/tools/web-scrapers/vscode-marketplace.ts +195 -0
- package/src/core/tools/web-scrapers/w3c.ts +163 -0
- package/src/core/tools/web-scrapers/wikidata.ts +357 -0
- package/src/core/tools/web-scrapers/wikipedia.test.ts +73 -0
- package/src/core/tools/web-scrapers/wikipedia.ts +95 -0
- package/src/core/tools/web-scrapers/youtube.test.ts +198 -0
- package/src/core/tools/web-scrapers/youtube.ts +371 -0
- package/src/core/tools/write.ts +21 -18
- package/src/core/voice.ts +3 -2
- package/src/lib/worktree/collapse.ts +2 -1
- package/src/lib/worktree/git.ts +2 -18
- package/src/main.ts +59 -3
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +33 -19
- package/src/modes/interactive/components/extensions/extension-list.ts +15 -8
- package/src/modes/interactive/components/hook-editor.ts +2 -1
- package/src/modes/interactive/components/model-selector.ts +19 -4
- package/src/modes/interactive/interactive-mode.ts +41 -38
- package/src/modes/interactive/theme/theme.ts +58 -58
- package/src/modes/rpc/rpc-mode.ts +10 -9
- package/src/prompts/review-request.md +27 -0
- package/src/prompts/reviewer.md +64 -68
- package/src/prompts/tools/output.md +22 -3
- package/src/prompts/tools/task.md +32 -33
- package/src/utils/clipboard.ts +2 -1
- package/src/utils/tools-manager.ts +110 -8
- package/examples/extensions/subagent/agents/reviewer.md +0 -35
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
1
|
import { homedir } from "node:os";
|
|
3
|
-
import { extname, join } from "node:path";
|
|
2
|
+
import { basename, extname, join } from "node:path";
|
|
3
|
+
import { globSync } from "glob";
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
4
5
|
import { getConfigDirPaths } from "../../../config";
|
|
6
|
+
import { logger } from "../../logger";
|
|
5
7
|
import { createBiomeClient } from "./clients/biome-client";
|
|
8
|
+
import DEFAULTS from "./defaults.json" with { type: "json" };
|
|
6
9
|
import type { ServerConfig } from "./types";
|
|
7
10
|
|
|
8
11
|
export interface LspConfig {
|
|
@@ -12,599 +15,138 @@ export interface LspConfig {
|
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
// =============================================================================
|
|
15
|
-
//
|
|
18
|
+
// Default Server Configuration Loading
|
|
16
19
|
// =============================================================================
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
// =========================================================================
|
|
150
|
-
// Web Technologies
|
|
151
|
-
// =========================================================================
|
|
152
|
-
|
|
153
|
-
"vscode-html-language-server": {
|
|
154
|
-
command: "vscode-html-language-server",
|
|
155
|
-
args: ["--stdio"],
|
|
156
|
-
fileTypes: [".html", ".htm"],
|
|
157
|
-
rootMarkers: ["package.json", ".git"],
|
|
158
|
-
initOptions: {
|
|
159
|
-
provideFormatter: true,
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
"vscode-css-language-server": {
|
|
164
|
-
command: "vscode-css-language-server",
|
|
165
|
-
args: ["--stdio"],
|
|
166
|
-
fileTypes: [".css", ".scss", ".sass", ".less"],
|
|
167
|
-
rootMarkers: ["package.json", ".git"],
|
|
168
|
-
initOptions: {
|
|
169
|
-
provideFormatter: true,
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
"vscode-json-language-server": {
|
|
174
|
-
command: "vscode-json-language-server",
|
|
175
|
-
args: ["--stdio"],
|
|
176
|
-
fileTypes: [".json", ".jsonc"],
|
|
177
|
-
rootMarkers: ["package.json", ".git"],
|
|
178
|
-
initOptions: {
|
|
179
|
-
provideFormatter: true,
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
tailwindcss: {
|
|
184
|
-
command: "tailwindcss-language-server",
|
|
185
|
-
args: ["--stdio"],
|
|
186
|
-
fileTypes: [".html", ".css", ".scss", ".js", ".jsx", ".ts", ".tsx", ".vue", ".svelte"],
|
|
187
|
-
rootMarkers: ["tailwind.config.js", "tailwind.config.ts", "tailwind.config.mjs", "tailwind.config.cjs"],
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
svelte: {
|
|
191
|
-
command: "svelteserver",
|
|
192
|
-
args: ["--stdio"],
|
|
193
|
-
fileTypes: [".svelte"],
|
|
194
|
-
rootMarkers: ["svelte.config.js", "svelte.config.mjs", "package.json"],
|
|
195
|
-
},
|
|
196
|
-
|
|
197
|
-
"vue-language-server": {
|
|
198
|
-
command: "vue-language-server",
|
|
199
|
-
args: ["--stdio"],
|
|
200
|
-
fileTypes: [".vue"],
|
|
201
|
-
rootMarkers: ["vue.config.js", "nuxt.config.js", "nuxt.config.ts", "package.json"],
|
|
202
|
-
},
|
|
203
|
-
|
|
204
|
-
astro: {
|
|
205
|
-
command: "astro-ls",
|
|
206
|
-
args: ["--stdio"],
|
|
207
|
-
fileTypes: [".astro"],
|
|
208
|
-
rootMarkers: ["astro.config.mjs", "astro.config.js", "astro.config.ts"],
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
// =========================================================================
|
|
212
|
-
// Python
|
|
213
|
-
// =========================================================================
|
|
214
|
-
|
|
215
|
-
pyright: {
|
|
216
|
-
command: "pyright-langserver",
|
|
217
|
-
args: ["--stdio"],
|
|
218
|
-
fileTypes: [".py", ".pyi"],
|
|
219
|
-
rootMarkers: ["pyproject.toml", "pyrightconfig.json", "setup.py", "setup.cfg", "requirements.txt", "Pipfile"],
|
|
220
|
-
settings: {
|
|
221
|
-
python: {
|
|
222
|
-
analysis: {
|
|
223
|
-
autoSearchPaths: true,
|
|
224
|
-
diagnosticMode: "openFilesOnly",
|
|
225
|
-
useLibraryCodeForTypes: true,
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
basedpyright: {
|
|
232
|
-
command: "basedpyright-langserver",
|
|
233
|
-
args: ["--stdio"],
|
|
234
|
-
fileTypes: [".py", ".pyi"],
|
|
235
|
-
rootMarkers: ["pyproject.toml", "pyrightconfig.json", "setup.py", "requirements.txt"],
|
|
236
|
-
settings: {
|
|
237
|
-
basedpyright: {
|
|
238
|
-
analysis: {
|
|
239
|
-
autoSearchPaths: true,
|
|
240
|
-
diagnosticMode: "openFilesOnly",
|
|
241
|
-
useLibraryCodeForTypes: true,
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
pylsp: {
|
|
248
|
-
command: "pylsp",
|
|
249
|
-
args: [],
|
|
250
|
-
fileTypes: [".py"],
|
|
251
|
-
rootMarkers: ["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile"],
|
|
252
|
-
},
|
|
253
|
-
|
|
254
|
-
ruff: {
|
|
255
|
-
command: "ruff",
|
|
256
|
-
args: ["server"],
|
|
257
|
-
fileTypes: [".py", ".pyi"],
|
|
258
|
-
rootMarkers: ["pyproject.toml", "ruff.toml", ".ruff.toml"],
|
|
259
|
-
isLinter: true,
|
|
260
|
-
},
|
|
261
|
-
|
|
262
|
-
// =========================================================================
|
|
263
|
-
// JVM Languages
|
|
264
|
-
// =========================================================================
|
|
265
|
-
|
|
266
|
-
jdtls: {
|
|
267
|
-
command: "jdtls",
|
|
268
|
-
args: [],
|
|
269
|
-
fileTypes: [".java"],
|
|
270
|
-
rootMarkers: ["pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", ".project"],
|
|
271
|
-
},
|
|
272
|
-
|
|
273
|
-
"kotlin-language-server": {
|
|
274
|
-
command: "kotlin-language-server",
|
|
275
|
-
args: [],
|
|
276
|
-
fileTypes: [".kt", ".kts"],
|
|
277
|
-
rootMarkers: ["build.gradle", "build.gradle.kts", "pom.xml", "settings.gradle", "settings.gradle.kts"],
|
|
278
|
-
},
|
|
279
|
-
|
|
280
|
-
metals: {
|
|
281
|
-
command: "metals",
|
|
282
|
-
args: [],
|
|
283
|
-
fileTypes: [".scala", ".sbt", ".sc"],
|
|
284
|
-
rootMarkers: ["build.sbt", "build.sc", "build.gradle", "pom.xml"],
|
|
285
|
-
initOptions: {
|
|
286
|
-
statusBarProvider: "show-message",
|
|
287
|
-
isHttpEnabled: true,
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
|
|
291
|
-
// =========================================================================
|
|
292
|
-
// Functional Languages
|
|
293
|
-
// =========================================================================
|
|
294
|
-
|
|
295
|
-
hls: {
|
|
296
|
-
command: "haskell-language-server-wrapper",
|
|
297
|
-
args: ["--lsp"],
|
|
298
|
-
fileTypes: [".hs", ".lhs"],
|
|
299
|
-
rootMarkers: ["stack.yaml", "cabal.project", "hie.yaml", "package.yaml", "*.cabal"],
|
|
300
|
-
settings: {
|
|
301
|
-
haskell: {
|
|
302
|
-
formattingProvider: "ormolu",
|
|
303
|
-
checkProject: true,
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
|
|
308
|
-
ocamllsp: {
|
|
309
|
-
command: "ocamllsp",
|
|
310
|
-
args: [],
|
|
311
|
-
fileTypes: [".ml", ".mli", ".mll", ".mly"],
|
|
312
|
-
rootMarkers: ["dune-project", "dune-workspace", "*.opam", ".ocamlformat"],
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
elixirls: {
|
|
316
|
-
command: "elixir-ls",
|
|
317
|
-
args: [],
|
|
318
|
-
fileTypes: [".ex", ".exs", ".heex", ".eex"],
|
|
319
|
-
rootMarkers: ["mix.exs", "mix.lock"],
|
|
320
|
-
settings: {
|
|
321
|
-
elixirLS: {
|
|
322
|
-
dialyzerEnabled: true,
|
|
323
|
-
fetchDeps: false,
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
|
|
328
|
-
erlangls: {
|
|
329
|
-
command: "erlang_ls",
|
|
330
|
-
args: [],
|
|
331
|
-
fileTypes: [".erl", ".hrl"],
|
|
332
|
-
rootMarkers: ["rebar.config", "erlang.mk", "rebar.lock"],
|
|
333
|
-
},
|
|
334
|
-
|
|
335
|
-
gleam: {
|
|
336
|
-
command: "gleam",
|
|
337
|
-
args: ["lsp"],
|
|
338
|
-
fileTypes: [".gleam"],
|
|
339
|
-
rootMarkers: ["gleam.toml"],
|
|
340
|
-
},
|
|
341
|
-
|
|
342
|
-
// =========================================================================
|
|
343
|
-
// Ruby
|
|
344
|
-
// =========================================================================
|
|
345
|
-
|
|
346
|
-
solargraph: {
|
|
347
|
-
command: "solargraph",
|
|
348
|
-
args: ["stdio"],
|
|
349
|
-
fileTypes: [".rb", ".rake", ".gemspec"],
|
|
350
|
-
rootMarkers: ["Gemfile", ".solargraph.yml", "Rakefile"],
|
|
351
|
-
initOptions: {
|
|
352
|
-
formatting: true,
|
|
353
|
-
},
|
|
354
|
-
settings: {
|
|
355
|
-
solargraph: {
|
|
356
|
-
diagnostics: true,
|
|
357
|
-
completion: true,
|
|
358
|
-
hover: true,
|
|
359
|
-
formatting: true,
|
|
360
|
-
references: true,
|
|
361
|
-
rename: true,
|
|
362
|
-
symbols: true,
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
|
|
367
|
-
"ruby-lsp": {
|
|
368
|
-
command: "ruby-lsp",
|
|
369
|
-
args: [],
|
|
370
|
-
fileTypes: [".rb", ".rake", ".gemspec", ".erb"],
|
|
371
|
-
rootMarkers: ["Gemfile", ".ruby-version", ".ruby-gemset"],
|
|
372
|
-
initOptions: {
|
|
373
|
-
formatter: "auto",
|
|
374
|
-
},
|
|
375
|
-
},
|
|
376
|
-
|
|
377
|
-
rubocop: {
|
|
378
|
-
command: "rubocop",
|
|
379
|
-
args: ["--lsp"],
|
|
380
|
-
fileTypes: [".rb", ".rake"],
|
|
381
|
-
rootMarkers: [".rubocop.yml", "Gemfile"],
|
|
382
|
-
isLinter: true,
|
|
383
|
-
},
|
|
384
|
-
|
|
385
|
-
// =========================================================================
|
|
386
|
-
// Shell / Scripting
|
|
387
|
-
// =========================================================================
|
|
388
|
-
|
|
389
|
-
bashls: {
|
|
390
|
-
command: "bash-language-server",
|
|
391
|
-
args: ["start"],
|
|
392
|
-
fileTypes: [".sh", ".bash", ".zsh"],
|
|
393
|
-
rootMarkers: [".git"],
|
|
394
|
-
settings: {
|
|
395
|
-
bashIde: {
|
|
396
|
-
globPattern: "*@(.sh|.inc|.bash|.command)",
|
|
397
|
-
},
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
|
|
401
|
-
nushell: {
|
|
402
|
-
command: "nu",
|
|
403
|
-
args: ["--lsp"],
|
|
404
|
-
fileTypes: [".nu"],
|
|
405
|
-
rootMarkers: [".git"],
|
|
406
|
-
},
|
|
407
|
-
|
|
408
|
-
// =========================================================================
|
|
409
|
-
// Lua
|
|
410
|
-
// =========================================================================
|
|
411
|
-
|
|
412
|
-
"lua-language-server": {
|
|
413
|
-
command: "lua-language-server",
|
|
414
|
-
args: [],
|
|
415
|
-
fileTypes: [".lua"],
|
|
416
|
-
rootMarkers: [".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml"],
|
|
417
|
-
settings: {
|
|
418
|
-
Lua: {
|
|
419
|
-
runtime: { version: "LuaJIT" },
|
|
420
|
-
diagnostics: { globals: ["vim"] },
|
|
421
|
-
workspace: { checkThirdParty: false },
|
|
422
|
-
telemetry: { enable: false },
|
|
423
|
-
},
|
|
424
|
-
},
|
|
425
|
-
},
|
|
426
|
-
|
|
427
|
-
// =========================================================================
|
|
428
|
-
// PHP
|
|
429
|
-
// =========================================================================
|
|
430
|
-
|
|
431
|
-
intelephense: {
|
|
432
|
-
command: "intelephense",
|
|
433
|
-
args: ["--stdio"],
|
|
434
|
-
fileTypes: [".php", ".phtml"],
|
|
435
|
-
rootMarkers: ["composer.json", "composer.lock", ".git"],
|
|
436
|
-
},
|
|
437
|
-
|
|
438
|
-
phpactor: {
|
|
439
|
-
command: "phpactor",
|
|
440
|
-
args: ["language-server"],
|
|
441
|
-
fileTypes: [".php"],
|
|
442
|
-
rootMarkers: ["composer.json", ".phpactor.json", ".phpactor.yml"],
|
|
443
|
-
},
|
|
444
|
-
|
|
445
|
-
// =========================================================================
|
|
446
|
-
// .NET
|
|
447
|
-
// =========================================================================
|
|
448
|
-
|
|
449
|
-
omnisharp: {
|
|
450
|
-
command: "omnisharp",
|
|
451
|
-
args: ["-z", "--hostPID", String(process.pid), "--encoding", "utf-8", "--languageserver"],
|
|
452
|
-
fileTypes: [".cs", ".csx"],
|
|
453
|
-
rootMarkers: ["*.sln", "*.csproj", "omnisharp.json", ".git"],
|
|
454
|
-
settings: {
|
|
455
|
-
FormattingOptions: { EnableEditorConfigSupport: true },
|
|
456
|
-
RoslynExtensionsOptions: { EnableAnalyzersSupport: true },
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
|
|
460
|
-
// =========================================================================
|
|
461
|
-
// Configuration Languages
|
|
462
|
-
// =========================================================================
|
|
463
|
-
|
|
464
|
-
yamlls: {
|
|
465
|
-
command: "yaml-language-server",
|
|
466
|
-
args: ["--stdio"],
|
|
467
|
-
fileTypes: [".yaml", ".yml"],
|
|
468
|
-
rootMarkers: [".git"],
|
|
469
|
-
settings: {
|
|
470
|
-
yaml: {
|
|
471
|
-
validate: true,
|
|
472
|
-
format: { enable: true },
|
|
473
|
-
hover: true,
|
|
474
|
-
completion: true,
|
|
475
|
-
},
|
|
476
|
-
redhat: { telemetry: { enabled: false } },
|
|
477
|
-
},
|
|
478
|
-
},
|
|
479
|
-
|
|
480
|
-
taplo: {
|
|
481
|
-
command: "taplo",
|
|
482
|
-
args: ["lsp", "stdio"],
|
|
483
|
-
fileTypes: [".toml"],
|
|
484
|
-
rootMarkers: [".taplo.toml", "taplo.toml", ".git"],
|
|
485
|
-
},
|
|
486
|
-
|
|
487
|
-
terraformls: {
|
|
488
|
-
command: "terraform-ls",
|
|
489
|
-
args: ["serve"],
|
|
490
|
-
fileTypes: [".tf", ".tfvars"],
|
|
491
|
-
rootMarkers: [".terraform", "terraform.tfstate", "*.tf"],
|
|
492
|
-
},
|
|
493
|
-
|
|
494
|
-
dockerls: {
|
|
495
|
-
command: "docker-langserver",
|
|
496
|
-
args: ["--stdio"],
|
|
497
|
-
fileTypes: [".dockerfile"],
|
|
498
|
-
rootMarkers: ["Dockerfile", "docker-compose.yml", "docker-compose.yaml", ".dockerignore"],
|
|
499
|
-
},
|
|
500
|
-
|
|
501
|
-
"helm-ls": {
|
|
502
|
-
command: "helm_ls",
|
|
503
|
-
args: ["serve"],
|
|
504
|
-
fileTypes: [".yaml", ".yml", ".tpl"],
|
|
505
|
-
rootMarkers: ["Chart.yaml", "Chart.yml"],
|
|
506
|
-
},
|
|
507
|
-
|
|
508
|
-
// =========================================================================
|
|
509
|
-
// Nix
|
|
510
|
-
// =========================================================================
|
|
511
|
-
|
|
512
|
-
nixd: {
|
|
513
|
-
command: "nixd",
|
|
514
|
-
args: [],
|
|
515
|
-
fileTypes: [".nix"],
|
|
516
|
-
rootMarkers: ["flake.nix", "default.nix", "shell.nix"],
|
|
517
|
-
},
|
|
518
|
-
|
|
519
|
-
nil: {
|
|
520
|
-
command: "nil",
|
|
521
|
-
args: [],
|
|
522
|
-
fileTypes: [".nix"],
|
|
523
|
-
rootMarkers: ["flake.nix", "default.nix", "shell.nix"],
|
|
524
|
-
},
|
|
525
|
-
|
|
526
|
-
// =========================================================================
|
|
527
|
-
// Other Languages
|
|
528
|
-
// =========================================================================
|
|
529
|
-
|
|
530
|
-
ols: {
|
|
531
|
-
command: "ols",
|
|
532
|
-
args: [],
|
|
533
|
-
fileTypes: [".odin"],
|
|
534
|
-
rootMarkers: ["ols.json", ".git"],
|
|
535
|
-
},
|
|
536
|
-
|
|
537
|
-
dartls: {
|
|
538
|
-
command: "dart",
|
|
539
|
-
args: ["language-server", "--protocol=lsp"],
|
|
540
|
-
fileTypes: [".dart"],
|
|
541
|
-
rootMarkers: ["pubspec.yaml", "pubspec.lock"],
|
|
542
|
-
initOptions: {
|
|
543
|
-
closingLabels: true,
|
|
544
|
-
flutterOutline: true,
|
|
545
|
-
outline: true,
|
|
546
|
-
},
|
|
547
|
-
},
|
|
548
|
-
|
|
549
|
-
marksman: {
|
|
550
|
-
command: "marksman",
|
|
551
|
-
args: ["server"],
|
|
552
|
-
fileTypes: [".md", ".markdown"],
|
|
553
|
-
rootMarkers: [".marksman.toml", ".git"],
|
|
554
|
-
},
|
|
555
|
-
|
|
556
|
-
texlab: {
|
|
557
|
-
command: "texlab",
|
|
558
|
-
args: [],
|
|
559
|
-
fileTypes: [".tex", ".bib", ".sty", ".cls"],
|
|
560
|
-
rootMarkers: [".latexmkrc", "latexmkrc", ".texlabroot", "texlabroot", "Tectonic.toml"],
|
|
561
|
-
settings: {
|
|
562
|
-
texlab: {
|
|
563
|
-
build: {
|
|
564
|
-
executable: "latexmk",
|
|
565
|
-
args: ["-pdf", "-interaction=nonstopmode", "-synctex=1", "%f"],
|
|
566
|
-
},
|
|
567
|
-
chktex: { onOpenAndSave: true },
|
|
568
|
-
},
|
|
569
|
-
},
|
|
570
|
-
},
|
|
571
|
-
|
|
572
|
-
graphql: {
|
|
573
|
-
command: "graphql-lsp",
|
|
574
|
-
args: ["server", "-m", "stream"],
|
|
575
|
-
fileTypes: [".graphql", ".gql"],
|
|
576
|
-
rootMarkers: [".graphqlrc", ".graphqlrc.json", ".graphqlrc.yml", ".graphqlrc.yaml", "graphql.config.js"],
|
|
577
|
-
},
|
|
578
|
-
|
|
579
|
-
prismals: {
|
|
580
|
-
command: "prisma-language-server",
|
|
581
|
-
args: ["--stdio"],
|
|
582
|
-
fileTypes: [".prisma"],
|
|
583
|
-
rootMarkers: ["schema.prisma", "prisma/schema.prisma"],
|
|
584
|
-
},
|
|
585
|
-
|
|
586
|
-
vimls: {
|
|
587
|
-
command: "vim-language-server",
|
|
588
|
-
args: ["--stdio"],
|
|
589
|
-
fileTypes: [".vim", ".vimrc"],
|
|
590
|
-
rootMarkers: [".git"],
|
|
591
|
-
initOptions: {
|
|
592
|
-
isNeovim: true,
|
|
593
|
-
diagnostic: { enable: true },
|
|
594
|
-
},
|
|
595
|
-
},
|
|
596
|
-
|
|
597
|
-
// =========================================================================
|
|
598
|
-
// Emmet (HTML/CSS expansion)
|
|
599
|
-
// =========================================================================
|
|
600
|
-
|
|
601
|
-
"emmet-language-server": {
|
|
602
|
-
command: "emmet-language-server",
|
|
603
|
-
args: ["--stdio"],
|
|
604
|
-
fileTypes: [".html", ".css", ".scss", ".less", ".jsx", ".tsx", ".vue", ".svelte"],
|
|
605
|
-
rootMarkers: [".git"],
|
|
606
|
-
},
|
|
607
|
-
};
|
|
21
|
+
const PID_TOKEN = "$PID";
|
|
22
|
+
|
|
23
|
+
interface NormalizedConfig {
|
|
24
|
+
servers: Record<string, Partial<ServerConfig>>;
|
|
25
|
+
idleTimeoutMs?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
29
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseConfigContent(content: string, filePath: string): unknown {
|
|
33
|
+
const extension = extname(filePath).toLowerCase();
|
|
34
|
+
if (extension === ".yaml" || extension === ".yml") {
|
|
35
|
+
return parseYaml(content) as unknown;
|
|
36
|
+
}
|
|
37
|
+
return JSON.parse(content) as unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeConfig(value: unknown): NormalizedConfig | null {
|
|
41
|
+
if (!isRecord(value)) return null;
|
|
42
|
+
|
|
43
|
+
const idleTimeoutMs = typeof value.idleTimeoutMs === "number" ? value.idleTimeoutMs : undefined;
|
|
44
|
+
const rawServers = value.servers;
|
|
45
|
+
|
|
46
|
+
if (isRecord(rawServers)) {
|
|
47
|
+
return { servers: rawServers as Record<string, Partial<ServerConfig>>, idleTimeoutMs };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const servers = Object.fromEntries(Object.entries(value).filter(([key]) => key !== "idleTimeoutMs")) as Record<
|
|
51
|
+
string,
|
|
52
|
+
Partial<ServerConfig>
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
return { servers, idleTimeoutMs };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeStringArray(value: unknown): string[] | null {
|
|
59
|
+
if (!Array.isArray(value)) return null;
|
|
60
|
+
const items = value.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
|
|
61
|
+
return items.length > 0 ? items : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeServerConfig(name: string, config: Partial<ServerConfig>): ServerConfig | null {
|
|
65
|
+
const command = typeof config.command === "string" && config.command.length > 0 ? config.command : null;
|
|
66
|
+
const fileTypes = normalizeStringArray(config.fileTypes);
|
|
67
|
+
const rootMarkers = normalizeStringArray(config.rootMarkers);
|
|
68
|
+
|
|
69
|
+
if (!command || !fileTypes || !rootMarkers) {
|
|
70
|
+
logger.warn("Ignoring invalid LSP server config (missing required fields).", { name });
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const args = Array.isArray(config.args)
|
|
75
|
+
? config.args.filter((entry): entry is string => typeof entry === "string")
|
|
76
|
+
: undefined;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
...config,
|
|
80
|
+
command,
|
|
81
|
+
args,
|
|
82
|
+
fileTypes,
|
|
83
|
+
rootMarkers,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function readConfigFile(filePath: string): Promise<NormalizedConfig | null> {
|
|
88
|
+
try {
|
|
89
|
+
const file = Bun.file(filePath);
|
|
90
|
+
if (!(await file.exists())) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const content = await file.text();
|
|
94
|
+
const parsed = parseConfigContent(content, filePath);
|
|
95
|
+
return normalizeConfig(parsed);
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function coerceServerConfigs(servers: Record<string, Partial<ServerConfig>>): Record<string, ServerConfig> {
|
|
102
|
+
const result: Record<string, ServerConfig> = {};
|
|
103
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
104
|
+
const normalized = normalizeServerConfig(name, config);
|
|
105
|
+
if (normalized) {
|
|
106
|
+
result[name] = normalized;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function mergeServers(
|
|
113
|
+
base: Record<string, ServerConfig>,
|
|
114
|
+
overrides: Record<string, Partial<ServerConfig>>,
|
|
115
|
+
): Record<string, ServerConfig> {
|
|
116
|
+
const merged: Record<string, ServerConfig> = { ...base };
|
|
117
|
+
for (const [name, config] of Object.entries(overrides)) {
|
|
118
|
+
if (merged[name]) {
|
|
119
|
+
const candidate = { ...merged[name], ...config };
|
|
120
|
+
const normalized = normalizeServerConfig(name, candidate);
|
|
121
|
+
if (normalized) {
|
|
122
|
+
merged[name] = normalized;
|
|
123
|
+
} else {
|
|
124
|
+
logger.warn("Ignoring invalid LSP overrides (keeping previous config).", { name });
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
const normalized = normalizeServerConfig(name, config);
|
|
128
|
+
if (normalized) {
|
|
129
|
+
merged[name] = normalized;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return merged;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function applyRuntimeDefaults(servers: Record<string, ServerConfig>): Record<string, ServerConfig> {
|
|
137
|
+
const updated: Record<string, ServerConfig> = { ...servers };
|
|
138
|
+
|
|
139
|
+
if (updated.biome) {
|
|
140
|
+
updated.biome = { ...updated.biome, createClient: createBiomeClient };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (updated.omnisharp?.args) {
|
|
144
|
+
const args = updated.omnisharp.args.map((arg) => (arg === PID_TOKEN ? String(process.pid) : arg));
|
|
145
|
+
updated.omnisharp = { ...updated.omnisharp, args };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return updated;
|
|
149
|
+
}
|
|
608
150
|
|
|
609
151
|
// =============================================================================
|
|
610
152
|
// Configuration Loading
|
|
@@ -613,22 +155,26 @@ export const SERVERS: Record<string, ServerConfig> = {
|
|
|
613
155
|
/**
|
|
614
156
|
* Check if any root marker file exists in the directory
|
|
615
157
|
*/
|
|
616
|
-
export function hasRootMarkers(cwd: string, markers: string[]): boolean {
|
|
617
|
-
|
|
158
|
+
export async function hasRootMarkers(cwd: string, markers: string[]): Promise<boolean> {
|
|
159
|
+
for (const marker of markers) {
|
|
618
160
|
// Handle glob-like patterns (e.g., "*.cabal")
|
|
619
161
|
if (marker.includes("*")) {
|
|
620
162
|
try {
|
|
621
|
-
const { globSync } = require("glob");
|
|
622
163
|
const matches = globSync(join(cwd, marker));
|
|
623
|
-
|
|
164
|
+
if (matches.length > 0) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
624
167
|
} catch {
|
|
625
|
-
|
|
626
|
-
return false;
|
|
168
|
+
logger.warn("Failed to resolve glob root marker.", { marker, cwd });
|
|
627
169
|
}
|
|
170
|
+
continue;
|
|
628
171
|
}
|
|
629
172
|
const filePath = join(cwd, marker);
|
|
630
|
-
|
|
631
|
-
|
|
173
|
+
if (await Bun.file(filePath).exists()) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
632
178
|
}
|
|
633
179
|
|
|
634
180
|
// =============================================================================
|
|
@@ -661,12 +207,12 @@ const LOCAL_BIN_PATHS: Array<{ markers: string[]; binDir: string }> = [
|
|
|
661
207
|
* @param cwd - Working directory to search from
|
|
662
208
|
* @returns Absolute path to the executable, or null if not found
|
|
663
209
|
*/
|
|
664
|
-
export function resolveCommand(command: string, cwd: string): string | null {
|
|
210
|
+
export async function resolveCommand(command: string, cwd: string): Promise<string | null> {
|
|
665
211
|
// Check local bin directories based on project markers
|
|
666
212
|
for (const { markers, binDir } of LOCAL_BIN_PATHS) {
|
|
667
|
-
if (hasRootMarkers(cwd, markers)) {
|
|
213
|
+
if (await hasRootMarkers(cwd, markers)) {
|
|
668
214
|
const localPath = join(cwd, binDir, command);
|
|
669
|
-
if (
|
|
215
|
+
if (await Bun.file(localPath).exists()) {
|
|
670
216
|
return localPath;
|
|
671
217
|
}
|
|
672
218
|
}
|
|
@@ -681,7 +227,7 @@ export function resolveCommand(command: string, cwd: string): string | null {
|
|
|
681
227
|
* Supports both visible and hidden variants at each config location.
|
|
682
228
|
*/
|
|
683
229
|
function getConfigPaths(cwd: string): string[] {
|
|
684
|
-
const filenames = ["lsp.json", ".lsp.json"];
|
|
230
|
+
const filenames = ["lsp.json", ".lsp.json", "lsp.yaml", ".lsp.yaml", "lsp.yml", ".lsp.yml"];
|
|
685
231
|
const paths: string[] = [];
|
|
686
232
|
|
|
687
233
|
// Project root files (highest priority)
|
|
@@ -716,14 +262,16 @@ function getConfigPaths(cwd: string): string[] {
|
|
|
716
262
|
/**
|
|
717
263
|
* Load LSP configuration.
|
|
718
264
|
*
|
|
719
|
-
* Priority:
|
|
720
|
-
* 1. Project root: lsp.json
|
|
721
|
-
* 2. Project config dirs: .omp/lsp
|
|
722
|
-
* 3. User config dirs: ~/.omp/agent/lsp
|
|
723
|
-
* 4. User home root: ~/lsp
|
|
265
|
+
* Priority (highest to lowest):
|
|
266
|
+
* 1. Project root: lsp.json/.lsp.json/lsp.yml/.lsp.yml/lsp.yaml/.lsp.yaml
|
|
267
|
+
* 2. Project config dirs: .omp/lsp.*, .pi/lsp.*, .claude/lsp.* (+ hidden variants)
|
|
268
|
+
* 3. User config dirs: ~/.omp/agent/lsp.*, ~/.pi/agent/lsp.*, ~/.claude/lsp.* (+ hidden variants)
|
|
269
|
+
* 4. User home root: ~/lsp.*, ~/.lsp.*
|
|
724
270
|
* 5. Auto-detect from project markers + available binaries
|
|
725
271
|
*
|
|
726
|
-
* Config
|
|
272
|
+
* Config files are merged from lowest to highest priority; later files override earlier settings.
|
|
273
|
+
*
|
|
274
|
+
* Config file format (JSON or YAML):
|
|
727
275
|
* ```json
|
|
728
276
|
* {
|
|
729
277
|
* "servers": {
|
|
@@ -743,61 +291,56 @@ function getConfigPaths(cwd: string): string[] {
|
|
|
743
291
|
* ```
|
|
744
292
|
*/
|
|
745
293
|
export async function loadConfig(cwd: string): Promise<LspConfig> {
|
|
746
|
-
|
|
294
|
+
let mergedServers = coerceServerConfigs(DEFAULTS);
|
|
295
|
+
|
|
296
|
+
const configPaths = getConfigPaths(cwd).reverse();
|
|
297
|
+
let hasOverrides = false;
|
|
747
298
|
|
|
299
|
+
let idleTimeoutMs: number | undefined;
|
|
748
300
|
for (const configPath of configPaths) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
for (const [name, config] of Object.entries(servers) as [string, Partial<ServerConfig>][]) {
|
|
761
|
-
if (merged[name]) {
|
|
762
|
-
// Merge with existing config
|
|
763
|
-
merged[name] = { ...merged[name], ...config };
|
|
764
|
-
} else {
|
|
765
|
-
// Add new server config
|
|
766
|
-
merged[name] = config as ServerConfig;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
301
|
+
const parsed = await readConfigFile(configPath);
|
|
302
|
+
if (!parsed) continue;
|
|
303
|
+
const hasServerOverrides = Object.keys(parsed.servers).length > 0;
|
|
304
|
+
if (hasServerOverrides) {
|
|
305
|
+
hasOverrides = true;
|
|
306
|
+
mergedServers = mergeServers(mergedServers, parsed.servers);
|
|
307
|
+
}
|
|
308
|
+
if (parsed.idleTimeoutMs !== undefined) {
|
|
309
|
+
idleTimeoutMs = parsed.idleTimeoutMs;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
769
312
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
const resolved = resolveCommand(config.command, cwd);
|
|
775
|
-
if (!resolved) continue;
|
|
776
|
-
available[name] = { ...config, resolvedCommand: resolved };
|
|
777
|
-
}
|
|
313
|
+
if (!hasOverrides) {
|
|
314
|
+
// Auto-detect: find servers based on project markers AND available binaries
|
|
315
|
+
const detected: Record<string, ServerConfig> = {};
|
|
316
|
+
const defaultsWithRuntime = applyRuntimeDefaults(mergedServers);
|
|
778
317
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
318
|
+
for (const [name, config] of Object.entries(defaultsWithRuntime)) {
|
|
319
|
+
// Check if project has root markers for this language
|
|
320
|
+
if (!(await hasRootMarkers(cwd, config.rootMarkers))) continue;
|
|
321
|
+
|
|
322
|
+
// Check if the language server binary is available (local or $PATH)
|
|
323
|
+
const resolved = await resolveCommand(config.command, cwd);
|
|
324
|
+
if (!resolved) continue;
|
|
325
|
+
|
|
326
|
+
detected[name] = { ...config, resolvedCommand: resolved };
|
|
783
327
|
}
|
|
784
|
-
}
|
|
785
328
|
|
|
786
|
-
|
|
787
|
-
|
|
329
|
+
return { servers: detected, idleTimeoutMs };
|
|
330
|
+
}
|
|
788
331
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
332
|
+
// Merge overrides with defaults and filter to available servers
|
|
333
|
+
const mergedWithRuntime = applyRuntimeDefaults(mergedServers);
|
|
334
|
+
const available: Record<string, ServerConfig> = {};
|
|
792
335
|
|
|
793
|
-
|
|
794
|
-
|
|
336
|
+
for (const [name, config] of Object.entries(mergedWithRuntime)) {
|
|
337
|
+
if (config.disabled) continue;
|
|
338
|
+
const resolved = await resolveCommand(config.command, cwd);
|
|
795
339
|
if (!resolved) continue;
|
|
796
|
-
|
|
797
|
-
detected[name] = { ...config, resolvedCommand: resolved };
|
|
340
|
+
available[name] = { ...config, resolvedCommand: resolved };
|
|
798
341
|
}
|
|
799
342
|
|
|
800
|
-
return { servers:
|
|
343
|
+
return { servers: available, idleTimeoutMs };
|
|
801
344
|
}
|
|
802
345
|
|
|
803
346
|
// =============================================================================
|
|
@@ -810,10 +353,16 @@ export async function loadConfig(cwd: string): Promise<LspConfig> {
|
|
|
810
353
|
*/
|
|
811
354
|
export function getServersForFile(config: LspConfig, filePath: string): Array<[string, ServerConfig]> {
|
|
812
355
|
const ext = extname(filePath).toLowerCase();
|
|
356
|
+
const fileName = basename(filePath).toLowerCase();
|
|
813
357
|
const matches: Array<[string, ServerConfig]> = [];
|
|
814
358
|
|
|
815
359
|
for (const [name, serverConfig] of Object.entries(config.servers)) {
|
|
816
|
-
|
|
360
|
+
const supportsFile = serverConfig.fileTypes.some((fileType) => {
|
|
361
|
+
const normalized = fileType.toLowerCase();
|
|
362
|
+
return normalized === ext || normalized === fileName;
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
if (supportsFile) {
|
|
817
366
|
matches.push([name, serverConfig]);
|
|
818
367
|
}
|
|
819
368
|
}
|