@oh-my-pi/pi-coding-agent 1.338.0 → 1.341.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 +60 -1
- package/package.json +3 -3
- package/src/cli/args.ts +8 -0
- package/src/core/agent-session.ts +32 -14
- package/src/core/export-html/index.ts +48 -15
- package/src/core/export-html/template.html +3 -11
- package/src/core/mcp/client.ts +43 -16
- package/src/core/mcp/config.ts +152 -6
- package/src/core/mcp/index.ts +6 -2
- package/src/core/mcp/loader.ts +30 -3
- package/src/core/mcp/manager.ts +69 -10
- package/src/core/mcp/types.ts +9 -3
- package/src/core/model-resolver.ts +101 -0
- package/src/core/sdk.ts +65 -18
- package/src/core/session-manager.ts +117 -14
- package/src/core/settings-manager.ts +107 -19
- package/src/core/title-generator.ts +94 -0
- package/src/core/tools/bash.ts +1 -2
- package/src/core/tools/edit-diff.ts +2 -2
- package/src/core/tools/edit.ts +43 -5
- package/src/core/tools/grep.ts +3 -2
- package/src/core/tools/index.ts +73 -13
- package/src/core/tools/lsp/client.ts +45 -20
- package/src/core/tools/lsp/config.ts +708 -34
- package/src/core/tools/lsp/index.ts +423 -23
- package/src/core/tools/lsp/types.ts +5 -0
- package/src/core/tools/task/bundled-agents/explore.md +1 -1
- package/src/core/tools/task/bundled-agents/reviewer.md +1 -1
- package/src/core/tools/task/model-resolver.ts +52 -3
- package/src/core/tools/write.ts +67 -4
- package/src/index.ts +5 -0
- package/src/main.ts +23 -2
- package/src/modes/interactive/components/model-selector.ts +96 -18
- package/src/modes/interactive/components/session-selector.ts +20 -7
- package/src/modes/interactive/components/settings-defs.ts +59 -2
- package/src/modes/interactive/components/settings-selector.ts +8 -11
- package/src/modes/interactive/components/tool-execution.ts +18 -0
- package/src/modes/interactive/components/tree-selector.ts +2 -2
- package/src/modes/interactive/components/welcome.ts +40 -3
- package/src/modes/interactive/interactive-mode.ts +87 -10
- package/src/core/export-html/vendor/highlight.min.js +0 -1213
- package/src/core/export-html/vendor/marked.min.js +0 -6
|
@@ -5,10 +5,32 @@ import type { ServerConfig } from "./types.js";
|
|
|
5
5
|
|
|
6
6
|
export interface LspConfig {
|
|
7
7
|
servers: Record<string, ServerConfig>;
|
|
8
|
+
/** Idle timeout in milliseconds. If set, LSP clients will be shutdown after this period of inactivity. Disabled by default. */
|
|
9
|
+
idleTimeoutMs?: number;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
|
-
//
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Predefined Server Configurations
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Comprehensive LSP server configurations.
|
|
18
|
+
*
|
|
19
|
+
* Each server can be customized via lsp.json config file with these options:
|
|
20
|
+
* - command: Binary name or path
|
|
21
|
+
* - args: Command line arguments
|
|
22
|
+
* - fileTypes: File extensions this server handles
|
|
23
|
+
* - rootMarkers: Files that indicate project root
|
|
24
|
+
* - initOptions: LSP initialization options
|
|
25
|
+
* - settings: LSP workspace settings
|
|
26
|
+
* - disabled: Set to true to disable this server
|
|
27
|
+
* - isLinter: If true, used only for diagnostics/actions (not type intelligence)
|
|
28
|
+
*/
|
|
11
29
|
export const SERVERS: Record<string, ServerConfig> = {
|
|
30
|
+
// =========================================================================
|
|
31
|
+
// Systems Languages
|
|
32
|
+
// =========================================================================
|
|
33
|
+
|
|
12
34
|
"rust-analyzer": {
|
|
13
35
|
command: "rust-analyzer",
|
|
14
36
|
args: [],
|
|
@@ -19,6 +41,12 @@ export const SERVERS: Record<string, ServerConfig> = {
|
|
|
19
41
|
cargo: { allFeatures: true },
|
|
20
42
|
procMacro: { enable: true },
|
|
21
43
|
},
|
|
44
|
+
settings: {
|
|
45
|
+
"rust-analyzer": {
|
|
46
|
+
diagnostics: { enable: true },
|
|
47
|
+
inlayHints: { enable: true },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
22
50
|
capabilities: {
|
|
23
51
|
flycheck: true,
|
|
24
52
|
ssr: true,
|
|
@@ -27,81 +55,705 @@ export const SERVERS: Record<string, ServerConfig> = {
|
|
|
27
55
|
relatedTests: true,
|
|
28
56
|
},
|
|
29
57
|
},
|
|
58
|
+
|
|
59
|
+
clangd: {
|
|
60
|
+
command: "clangd",
|
|
61
|
+
args: ["--background-index", "--clang-tidy", "--header-insertion=iwyu"],
|
|
62
|
+
fileTypes: [".c", ".cpp", ".cc", ".cxx", ".h", ".hpp", ".hxx", ".m", ".mm"],
|
|
63
|
+
rootMarkers: ["compile_commands.json", "CMakeLists.txt", ".clangd", ".clang-format", "Makefile"],
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
zls: {
|
|
67
|
+
command: "zls",
|
|
68
|
+
args: [],
|
|
69
|
+
fileTypes: [".zig"],
|
|
70
|
+
rootMarkers: ["build.zig", "build.zig.zon", "zls.json"],
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
gopls: {
|
|
74
|
+
command: "gopls",
|
|
75
|
+
args: ["serve"],
|
|
76
|
+
fileTypes: [".go", ".mod", ".sum"],
|
|
77
|
+
rootMarkers: ["go.mod", "go.work", "go.sum"],
|
|
78
|
+
settings: {
|
|
79
|
+
gopls: {
|
|
80
|
+
analyses: { unusedparams: true, shadow: true },
|
|
81
|
+
staticcheck: true,
|
|
82
|
+
gofumpt: true,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// =========================================================================
|
|
88
|
+
// JavaScript/TypeScript Ecosystem
|
|
89
|
+
// =========================================================================
|
|
90
|
+
|
|
30
91
|
"typescript-language-server": {
|
|
31
92
|
command: "typescript-language-server",
|
|
32
93
|
args: ["--stdio"],
|
|
33
|
-
fileTypes: [".ts", ".tsx", ".js", ".jsx"],
|
|
94
|
+
fileTypes: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
|
|
34
95
|
rootMarkers: ["package.json", "tsconfig.json", "jsconfig.json"],
|
|
96
|
+
initOptions: {
|
|
97
|
+
hostInfo: "pi-coding-agent",
|
|
98
|
+
preferences: {
|
|
99
|
+
includeInlayParameterNameHints: "all",
|
|
100
|
+
includeInlayVariableTypeHints: true,
|
|
101
|
+
includeInlayFunctionParameterTypeHints: true,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
35
104
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
105
|
+
|
|
106
|
+
biome: {
|
|
107
|
+
command: "biome",
|
|
108
|
+
args: ["lsp-proxy"],
|
|
109
|
+
fileTypes: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ".jsonc"],
|
|
110
|
+
rootMarkers: ["biome.json", "biome.jsonc"],
|
|
111
|
+
isLinter: true,
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
eslint: {
|
|
115
|
+
command: "vscode-eslint-language-server",
|
|
116
|
+
args: ["--stdio"],
|
|
117
|
+
fileTypes: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".vue", ".svelte"],
|
|
118
|
+
rootMarkers: [
|
|
119
|
+
".eslintrc",
|
|
120
|
+
".eslintrc.js",
|
|
121
|
+
".eslintrc.json",
|
|
122
|
+
".eslintrc.yml",
|
|
123
|
+
"eslint.config.js",
|
|
124
|
+
"eslint.config.mjs",
|
|
125
|
+
],
|
|
126
|
+
isLinter: true,
|
|
127
|
+
settings: {
|
|
128
|
+
validate: "on",
|
|
129
|
+
run: "onType",
|
|
130
|
+
},
|
|
41
131
|
},
|
|
132
|
+
|
|
133
|
+
denols: {
|
|
134
|
+
command: "deno",
|
|
135
|
+
args: ["lsp"],
|
|
136
|
+
fileTypes: [".ts", ".tsx", ".js", ".jsx"],
|
|
137
|
+
rootMarkers: ["deno.json", "deno.jsonc", "deno.lock"],
|
|
138
|
+
initOptions: {
|
|
139
|
+
enable: true,
|
|
140
|
+
lint: true,
|
|
141
|
+
unstable: true,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// =========================================================================
|
|
146
|
+
// Web Technologies
|
|
147
|
+
// =========================================================================
|
|
148
|
+
|
|
149
|
+
"vscode-html-language-server": {
|
|
150
|
+
command: "vscode-html-language-server",
|
|
151
|
+
args: ["--stdio"],
|
|
152
|
+
fileTypes: [".html", ".htm"],
|
|
153
|
+
rootMarkers: ["package.json", ".git"],
|
|
154
|
+
initOptions: {
|
|
155
|
+
provideFormatter: true,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
"vscode-css-language-server": {
|
|
160
|
+
command: "vscode-css-language-server",
|
|
161
|
+
args: ["--stdio"],
|
|
162
|
+
fileTypes: [".css", ".scss", ".sass", ".less"],
|
|
163
|
+
rootMarkers: ["package.json", ".git"],
|
|
164
|
+
initOptions: {
|
|
165
|
+
provideFormatter: true,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
"vscode-json-language-server": {
|
|
170
|
+
command: "vscode-json-language-server",
|
|
171
|
+
args: ["--stdio"],
|
|
172
|
+
fileTypes: [".json", ".jsonc"],
|
|
173
|
+
rootMarkers: ["package.json", ".git"],
|
|
174
|
+
initOptions: {
|
|
175
|
+
provideFormatter: true,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
tailwindcss: {
|
|
180
|
+
command: "tailwindcss-language-server",
|
|
181
|
+
args: ["--stdio"],
|
|
182
|
+
fileTypes: [".html", ".css", ".scss", ".js", ".jsx", ".ts", ".tsx", ".vue", ".svelte"],
|
|
183
|
+
rootMarkers: ["tailwind.config.js", "tailwind.config.ts", "tailwind.config.mjs", "tailwind.config.cjs"],
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
svelte: {
|
|
187
|
+
command: "svelteserver",
|
|
188
|
+
args: ["--stdio"],
|
|
189
|
+
fileTypes: [".svelte"],
|
|
190
|
+
rootMarkers: ["svelte.config.js", "svelte.config.mjs", "package.json"],
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
"vue-language-server": {
|
|
194
|
+
command: "vue-language-server",
|
|
195
|
+
args: ["--stdio"],
|
|
196
|
+
fileTypes: [".vue"],
|
|
197
|
+
rootMarkers: ["vue.config.js", "nuxt.config.js", "nuxt.config.ts", "package.json"],
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
astro: {
|
|
201
|
+
command: "astro-ls",
|
|
202
|
+
args: ["--stdio"],
|
|
203
|
+
fileTypes: [".astro"],
|
|
204
|
+
rootMarkers: ["astro.config.mjs", "astro.config.js", "astro.config.ts"],
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
// =========================================================================
|
|
208
|
+
// Python
|
|
209
|
+
// =========================================================================
|
|
210
|
+
|
|
42
211
|
pyright: {
|
|
43
212
|
command: "pyright-langserver",
|
|
44
213
|
args: ["--stdio"],
|
|
214
|
+
fileTypes: [".py", ".pyi"],
|
|
215
|
+
rootMarkers: ["pyproject.toml", "pyrightconfig.json", "setup.py", "setup.cfg", "requirements.txt", "Pipfile"],
|
|
216
|
+
settings: {
|
|
217
|
+
python: {
|
|
218
|
+
analysis: {
|
|
219
|
+
autoSearchPaths: true,
|
|
220
|
+
diagnosticMode: "openFilesOnly",
|
|
221
|
+
useLibraryCodeForTypes: true,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
basedpyright: {
|
|
228
|
+
command: "basedpyright-langserver",
|
|
229
|
+
args: ["--stdio"],
|
|
230
|
+
fileTypes: [".py", ".pyi"],
|
|
231
|
+
rootMarkers: ["pyproject.toml", "pyrightconfig.json", "setup.py", "requirements.txt"],
|
|
232
|
+
settings: {
|
|
233
|
+
basedpyright: {
|
|
234
|
+
analysis: {
|
|
235
|
+
autoSearchPaths: true,
|
|
236
|
+
diagnosticMode: "openFilesOnly",
|
|
237
|
+
useLibraryCodeForTypes: true,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
pylsp: {
|
|
244
|
+
command: "pylsp",
|
|
245
|
+
args: [],
|
|
45
246
|
fileTypes: [".py"],
|
|
46
|
-
rootMarkers: ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"],
|
|
247
|
+
rootMarkers: ["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile"],
|
|
47
248
|
},
|
|
48
|
-
|
|
49
|
-
|
|
249
|
+
|
|
250
|
+
ruff: {
|
|
251
|
+
command: "ruff",
|
|
252
|
+
args: ["server"],
|
|
253
|
+
fileTypes: [".py", ".pyi"],
|
|
254
|
+
rootMarkers: ["pyproject.toml", "ruff.toml", ".ruff.toml"],
|
|
255
|
+
isLinter: true,
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
// =========================================================================
|
|
259
|
+
// JVM Languages
|
|
260
|
+
// =========================================================================
|
|
261
|
+
|
|
262
|
+
jdtls: {
|
|
263
|
+
command: "jdtls",
|
|
50
264
|
args: [],
|
|
51
|
-
fileTypes: [".
|
|
52
|
-
rootMarkers: ["build.
|
|
265
|
+
fileTypes: [".java"],
|
|
266
|
+
rootMarkers: ["pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", ".project"],
|
|
53
267
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
268
|
+
|
|
269
|
+
"kotlin-language-server": {
|
|
270
|
+
command: "kotlin-language-server",
|
|
271
|
+
args: [],
|
|
272
|
+
fileTypes: [".kt", ".kts"],
|
|
273
|
+
rootMarkers: ["build.gradle", "build.gradle.kts", "pom.xml", "settings.gradle", "settings.gradle.kts"],
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
metals: {
|
|
277
|
+
command: "metals",
|
|
278
|
+
args: [],
|
|
279
|
+
fileTypes: [".scala", ".sbt", ".sc"],
|
|
280
|
+
rootMarkers: ["build.sbt", "build.sc", "build.gradle", "pom.xml"],
|
|
281
|
+
initOptions: {
|
|
282
|
+
statusBarProvider: "show-message",
|
|
283
|
+
isHttpEnabled: true,
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
// =========================================================================
|
|
288
|
+
// Functional Languages
|
|
289
|
+
// =========================================================================
|
|
290
|
+
|
|
291
|
+
hls: {
|
|
292
|
+
command: "haskell-language-server-wrapper",
|
|
293
|
+
args: ["--lsp"],
|
|
294
|
+
fileTypes: [".hs", ".lhs"],
|
|
295
|
+
rootMarkers: ["stack.yaml", "cabal.project", "hie.yaml", "package.yaml", "*.cabal"],
|
|
296
|
+
settings: {
|
|
297
|
+
haskell: {
|
|
298
|
+
formattingProvider: "ormolu",
|
|
299
|
+
checkProject: true,
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
ocamllsp: {
|
|
305
|
+
command: "ocamllsp",
|
|
306
|
+
args: [],
|
|
307
|
+
fileTypes: [".ml", ".mli", ".mll", ".mly"],
|
|
308
|
+
rootMarkers: ["dune-project", "dune-workspace", "*.opam", ".ocamlformat"],
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
elixirls: {
|
|
312
|
+
command: "elixir-ls",
|
|
313
|
+
args: [],
|
|
314
|
+
fileTypes: [".ex", ".exs", ".heex", ".eex"],
|
|
315
|
+
rootMarkers: ["mix.exs", "mix.lock"],
|
|
316
|
+
settings: {
|
|
317
|
+
elixirLS: {
|
|
318
|
+
dialyzerEnabled: true,
|
|
319
|
+
fetchDeps: false,
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
erlangls: {
|
|
325
|
+
command: "erlang_ls",
|
|
326
|
+
args: [],
|
|
327
|
+
fileTypes: [".erl", ".hrl"],
|
|
328
|
+
rootMarkers: ["rebar.config", "erlang.mk", "rebar.lock"],
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
gleam: {
|
|
332
|
+
command: "gleam",
|
|
333
|
+
args: ["lsp"],
|
|
334
|
+
fileTypes: [".gleam"],
|
|
335
|
+
rootMarkers: ["gleam.toml"],
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
// =========================================================================
|
|
339
|
+
// Ruby
|
|
340
|
+
// =========================================================================
|
|
341
|
+
|
|
342
|
+
solargraph: {
|
|
343
|
+
command: "solargraph",
|
|
344
|
+
args: ["stdio"],
|
|
345
|
+
fileTypes: [".rb", ".rake", ".gemspec"],
|
|
346
|
+
rootMarkers: ["Gemfile", ".solargraph.yml", "Rakefile"],
|
|
347
|
+
initOptions: {
|
|
348
|
+
formatting: true,
|
|
349
|
+
},
|
|
350
|
+
settings: {
|
|
351
|
+
solargraph: {
|
|
352
|
+
diagnostics: true,
|
|
353
|
+
completion: true,
|
|
354
|
+
hover: true,
|
|
355
|
+
formatting: true,
|
|
356
|
+
references: true,
|
|
357
|
+
rename: true,
|
|
358
|
+
symbols: true,
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
"ruby-lsp": {
|
|
364
|
+
command: "ruby-lsp",
|
|
365
|
+
args: [],
|
|
366
|
+
fileTypes: [".rb", ".rake", ".gemspec", ".erb"],
|
|
367
|
+
rootMarkers: ["Gemfile", ".ruby-version", ".ruby-gemset"],
|
|
368
|
+
initOptions: {
|
|
369
|
+
formatter: "auto",
|
|
370
|
+
},
|
|
59
371
|
},
|
|
372
|
+
|
|
373
|
+
rubocop: {
|
|
374
|
+
command: "rubocop",
|
|
375
|
+
args: ["--lsp"],
|
|
376
|
+
fileTypes: [".rb", ".rake"],
|
|
377
|
+
rootMarkers: [".rubocop.yml", "Gemfile"],
|
|
378
|
+
isLinter: true,
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
// =========================================================================
|
|
382
|
+
// Shell / Scripting
|
|
383
|
+
// =========================================================================
|
|
384
|
+
|
|
385
|
+
bashls: {
|
|
386
|
+
command: "bash-language-server",
|
|
387
|
+
args: ["start"],
|
|
388
|
+
fileTypes: [".sh", ".bash", ".zsh"],
|
|
389
|
+
rootMarkers: [".git"],
|
|
390
|
+
settings: {
|
|
391
|
+
bashIde: {
|
|
392
|
+
globPattern: "*@(.sh|.inc|.bash|.command)",
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
nushell: {
|
|
398
|
+
command: "nu",
|
|
399
|
+
args: ["--lsp"],
|
|
400
|
+
fileTypes: [".nu"],
|
|
401
|
+
rootMarkers: [".git"],
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
// =========================================================================
|
|
405
|
+
// Lua
|
|
406
|
+
// =========================================================================
|
|
407
|
+
|
|
60
408
|
"lua-language-server": {
|
|
61
409
|
command: "lua-language-server",
|
|
62
410
|
args: [],
|
|
63
411
|
fileTypes: [".lua"],
|
|
64
|
-
rootMarkers: [".luarc.json", ".luarc.jsonc", ".luacheckrc"],
|
|
412
|
+
rootMarkers: [".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml"],
|
|
413
|
+
settings: {
|
|
414
|
+
Lua: {
|
|
415
|
+
runtime: { version: "LuaJIT" },
|
|
416
|
+
diagnostics: { globals: ["vim"] },
|
|
417
|
+
workspace: { checkThirdParty: false },
|
|
418
|
+
telemetry: { enable: false },
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
// =========================================================================
|
|
424
|
+
// PHP
|
|
425
|
+
// =========================================================================
|
|
426
|
+
|
|
427
|
+
intelephense: {
|
|
428
|
+
command: "intelephense",
|
|
429
|
+
args: ["--stdio"],
|
|
430
|
+
fileTypes: [".php", ".phtml"],
|
|
431
|
+
rootMarkers: ["composer.json", "composer.lock", ".git"],
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
phpactor: {
|
|
435
|
+
command: "phpactor",
|
|
436
|
+
args: ["language-server"],
|
|
437
|
+
fileTypes: [".php"],
|
|
438
|
+
rootMarkers: ["composer.json", ".phpactor.json", ".phpactor.yml"],
|
|
439
|
+
},
|
|
440
|
+
|
|
441
|
+
// =========================================================================
|
|
442
|
+
// .NET
|
|
443
|
+
// =========================================================================
|
|
444
|
+
|
|
445
|
+
omnisharp: {
|
|
446
|
+
command: "omnisharp",
|
|
447
|
+
args: ["-z", "--hostPID", String(process.pid), "--encoding", "utf-8", "--languageserver"],
|
|
448
|
+
fileTypes: [".cs", ".csx"],
|
|
449
|
+
rootMarkers: ["*.sln", "*.csproj", "omnisharp.json", ".git"],
|
|
450
|
+
settings: {
|
|
451
|
+
FormattingOptions: { EnableEditorConfigSupport: true },
|
|
452
|
+
RoslynExtensionsOptions: { EnableAnalyzersSupport: true },
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
// =========================================================================
|
|
457
|
+
// Configuration Languages
|
|
458
|
+
// =========================================================================
|
|
459
|
+
|
|
460
|
+
yamlls: {
|
|
461
|
+
command: "yaml-language-server",
|
|
462
|
+
args: ["--stdio"],
|
|
463
|
+
fileTypes: [".yaml", ".yml"],
|
|
464
|
+
rootMarkers: [".git"],
|
|
465
|
+
settings: {
|
|
466
|
+
yaml: {
|
|
467
|
+
validate: true,
|
|
468
|
+
format: { enable: true },
|
|
469
|
+
hover: true,
|
|
470
|
+
completion: true,
|
|
471
|
+
},
|
|
472
|
+
redhat: { telemetry: { enabled: false } },
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
taplo: {
|
|
477
|
+
command: "taplo",
|
|
478
|
+
args: ["lsp", "stdio"],
|
|
479
|
+
fileTypes: [".toml"],
|
|
480
|
+
rootMarkers: [".taplo.toml", "taplo.toml", ".git"],
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
terraformls: {
|
|
484
|
+
command: "terraform-ls",
|
|
485
|
+
args: ["serve"],
|
|
486
|
+
fileTypes: [".tf", ".tfvars"],
|
|
487
|
+
rootMarkers: [".terraform", "terraform.tfstate", "*.tf"],
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
dockerls: {
|
|
491
|
+
command: "docker-langserver",
|
|
492
|
+
args: ["--stdio"],
|
|
493
|
+
fileTypes: [".dockerfile"],
|
|
494
|
+
rootMarkers: ["Dockerfile", "docker-compose.yml", "docker-compose.yaml", ".dockerignore"],
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
"helm-ls": {
|
|
498
|
+
command: "helm_ls",
|
|
499
|
+
args: ["serve"],
|
|
500
|
+
fileTypes: [".yaml", ".yml", ".tpl"],
|
|
501
|
+
rootMarkers: ["Chart.yaml", "Chart.yml"],
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
// =========================================================================
|
|
505
|
+
// Nix
|
|
506
|
+
// =========================================================================
|
|
507
|
+
|
|
508
|
+
nixd: {
|
|
509
|
+
command: "nixd",
|
|
510
|
+
args: [],
|
|
511
|
+
fileTypes: [".nix"],
|
|
512
|
+
rootMarkers: ["flake.nix", "default.nix", "shell.nix"],
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
nil: {
|
|
516
|
+
command: "nil",
|
|
517
|
+
args: [],
|
|
518
|
+
fileTypes: [".nix"],
|
|
519
|
+
rootMarkers: ["flake.nix", "default.nix", "shell.nix"],
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
// =========================================================================
|
|
523
|
+
// Other Languages
|
|
524
|
+
// =========================================================================
|
|
525
|
+
|
|
526
|
+
ols: {
|
|
527
|
+
command: "ols",
|
|
528
|
+
args: [],
|
|
529
|
+
fileTypes: [".odin"],
|
|
530
|
+
rootMarkers: ["ols.json", ".git"],
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
dartls: {
|
|
534
|
+
command: "dart",
|
|
535
|
+
args: ["language-server", "--protocol=lsp"],
|
|
536
|
+
fileTypes: [".dart"],
|
|
537
|
+
rootMarkers: ["pubspec.yaml", "pubspec.lock"],
|
|
538
|
+
initOptions: {
|
|
539
|
+
closingLabels: true,
|
|
540
|
+
flutterOutline: true,
|
|
541
|
+
outline: true,
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
marksman: {
|
|
546
|
+
command: "marksman",
|
|
547
|
+
args: ["server"],
|
|
548
|
+
fileTypes: [".md", ".markdown"],
|
|
549
|
+
rootMarkers: [".marksman.toml", ".git"],
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
texlab: {
|
|
553
|
+
command: "texlab",
|
|
554
|
+
args: [],
|
|
555
|
+
fileTypes: [".tex", ".bib", ".sty", ".cls"],
|
|
556
|
+
rootMarkers: [".latexmkrc", "latexmkrc", ".texlabroot", "texlabroot", "Tectonic.toml"],
|
|
557
|
+
settings: {
|
|
558
|
+
texlab: {
|
|
559
|
+
build: {
|
|
560
|
+
executable: "latexmk",
|
|
561
|
+
args: ["-pdf", "-interaction=nonstopmode", "-synctex=1", "%f"],
|
|
562
|
+
},
|
|
563
|
+
chktex: { onOpenAndSave: true },
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
|
|
568
|
+
graphql: {
|
|
569
|
+
command: "graphql-lsp",
|
|
570
|
+
args: ["server", "-m", "stream"],
|
|
571
|
+
fileTypes: [".graphql", ".gql"],
|
|
572
|
+
rootMarkers: [".graphqlrc", ".graphqlrc.json", ".graphqlrc.yml", ".graphqlrc.yaml", "graphql.config.js"],
|
|
573
|
+
},
|
|
574
|
+
|
|
575
|
+
prismals: {
|
|
576
|
+
command: "prisma-language-server",
|
|
577
|
+
args: ["--stdio"],
|
|
578
|
+
fileTypes: [".prisma"],
|
|
579
|
+
rootMarkers: ["schema.prisma", "prisma/schema.prisma"],
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
vimls: {
|
|
583
|
+
command: "vim-language-server",
|
|
584
|
+
args: ["--stdio"],
|
|
585
|
+
fileTypes: [".vim", ".vimrc"],
|
|
586
|
+
rootMarkers: [".git"],
|
|
587
|
+
initOptions: {
|
|
588
|
+
isNeovim: true,
|
|
589
|
+
diagnostic: { enable: true },
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
|
|
593
|
+
// =========================================================================
|
|
594
|
+
// Emmet (HTML/CSS expansion)
|
|
595
|
+
// =========================================================================
|
|
596
|
+
|
|
597
|
+
"emmet-language-server": {
|
|
598
|
+
command: "emmet-language-server",
|
|
599
|
+
args: ["--stdio"],
|
|
600
|
+
fileTypes: [".html", ".css", ".scss", ".less", ".jsx", ".tsx", ".vue", ".svelte"],
|
|
601
|
+
rootMarkers: [".git"],
|
|
65
602
|
},
|
|
66
603
|
};
|
|
67
604
|
|
|
605
|
+
// =============================================================================
|
|
606
|
+
// Configuration Loading
|
|
607
|
+
// =============================================================================
|
|
608
|
+
|
|
68
609
|
/**
|
|
69
610
|
* Check if any root marker file exists in the directory
|
|
70
611
|
*/
|
|
71
612
|
export function hasRootMarkers(cwd: string, markers: string[]): boolean {
|
|
72
|
-
return markers.some((marker) =>
|
|
613
|
+
return markers.some((marker) => {
|
|
614
|
+
// Handle glob-like patterns (e.g., "*.cabal")
|
|
615
|
+
if (marker.includes("*")) {
|
|
616
|
+
try {
|
|
617
|
+
const { globSync } = require("node:fs");
|
|
618
|
+
const matches = globSync(join(cwd, marker));
|
|
619
|
+
return matches.length > 0;
|
|
620
|
+
} catch {
|
|
621
|
+
// globSync not available, skip glob patterns
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return existsSync(join(cwd, marker));
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// =============================================================================
|
|
630
|
+
// Local Binary Resolution
|
|
631
|
+
// =============================================================================
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Local bin directories to check before $PATH, ordered by priority.
|
|
635
|
+
* Each entry maps a root marker to the bin directory to check.
|
|
636
|
+
*/
|
|
637
|
+
const LOCAL_BIN_PATHS: Array<{ markers: string[]; binDir: string }> = [
|
|
638
|
+
// Node.js - check node_modules/.bin/
|
|
639
|
+
{ markers: ["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"], binDir: "node_modules/.bin" },
|
|
640
|
+
// Python - check virtual environment bin directories
|
|
641
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDir: ".venv/bin" },
|
|
642
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDir: "venv/bin" },
|
|
643
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDir: ".env/bin" },
|
|
644
|
+
// Ruby - check vendor bundle and binstubs
|
|
645
|
+
{ markers: ["Gemfile", "Gemfile.lock"], binDir: "vendor/bundle/bin" },
|
|
646
|
+
{ markers: ["Gemfile", "Gemfile.lock"], binDir: "bin" },
|
|
647
|
+
// Go - check project-local bin
|
|
648
|
+
{ markers: ["go.mod", "go.sum"], binDir: "bin" },
|
|
649
|
+
];
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Resolve a command to an executable path.
|
|
653
|
+
* Checks project-local bin directories first, then falls back to $PATH.
|
|
654
|
+
*
|
|
655
|
+
* @param command - The command name (e.g., "typescript-language-server")
|
|
656
|
+
* @param cwd - Working directory to search from
|
|
657
|
+
* @returns Absolute path to the executable, or null if not found
|
|
658
|
+
*/
|
|
659
|
+
export function resolveCommand(command: string, cwd: string): string | null {
|
|
660
|
+
// Check local bin directories based on project markers
|
|
661
|
+
for (const { markers, binDir } of LOCAL_BIN_PATHS) {
|
|
662
|
+
if (hasRootMarkers(cwd, markers)) {
|
|
663
|
+
const localPath = join(cwd, binDir, command);
|
|
664
|
+
if (existsSync(localPath)) {
|
|
665
|
+
return localPath;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Fall back to $PATH
|
|
671
|
+
return Bun.which(command);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Configuration file search paths (in priority order).
|
|
676
|
+
* Supports both visible and hidden variants, and both .pi subdirectory and root.
|
|
677
|
+
*/
|
|
678
|
+
function getConfigPaths(cwd: string): string[] {
|
|
679
|
+
return [
|
|
680
|
+
// Project-level configs (highest priority)
|
|
681
|
+
join(cwd, "lsp.json"),
|
|
682
|
+
join(cwd, ".lsp.json"),
|
|
683
|
+
join(cwd, ".pi", "lsp.json"),
|
|
684
|
+
join(cwd, ".pi", ".lsp.json"),
|
|
685
|
+
// User-level configs (fallback)
|
|
686
|
+
join(homedir(), ".pi", "lsp.json"),
|
|
687
|
+
join(homedir(), ".pi", ".lsp.json"),
|
|
688
|
+
join(homedir(), "lsp.json"),
|
|
689
|
+
join(homedir(), ".lsp.json"),
|
|
690
|
+
];
|
|
73
691
|
}
|
|
74
692
|
|
|
75
693
|
/**
|
|
76
694
|
* Load LSP configuration.
|
|
77
695
|
*
|
|
78
696
|
* Priority:
|
|
79
|
-
* 1. Project-level config
|
|
80
|
-
* 2. User-level config
|
|
697
|
+
* 1. Project-level config: lsp.json, .lsp.json, .pi/lsp.json, .pi/.lsp.json
|
|
698
|
+
* 2. User-level config: ~/.pi/lsp.json, ~/.pi/.lsp.json, ~/lsp.json, ~/.lsp.json
|
|
81
699
|
* 3. Auto-detect from project markers + available binaries
|
|
700
|
+
*
|
|
701
|
+
* Config file format:
|
|
702
|
+
* ```json
|
|
703
|
+
* {
|
|
704
|
+
* "servers": {
|
|
705
|
+
* "typescript-language-server": {
|
|
706
|
+
* "command": "typescript-language-server",
|
|
707
|
+
* "args": ["--stdio", "--log-level", "4"],
|
|
708
|
+
* "disabled": false
|
|
709
|
+
* },
|
|
710
|
+
* "my-custom-server": {
|
|
711
|
+
* "command": "/path/to/server",
|
|
712
|
+
* "args": ["--stdio"],
|
|
713
|
+
* "fileTypes": [".xyz"],
|
|
714
|
+
* "rootMarkers": [".xyz-project"]
|
|
715
|
+
* }
|
|
716
|
+
* }
|
|
717
|
+
* }
|
|
718
|
+
* ```
|
|
82
719
|
*/
|
|
83
720
|
export function loadConfig(cwd: string): LspConfig {
|
|
84
|
-
|
|
85
|
-
const configPaths = [join(cwd, ".pi", "lsp.json"), join(homedir(), ".pi", "lsp.json")];
|
|
721
|
+
const configPaths = getConfigPaths(cwd);
|
|
86
722
|
|
|
87
723
|
for (const configPath of configPaths) {
|
|
88
724
|
if (existsSync(configPath)) {
|
|
89
725
|
try {
|
|
90
726
|
const content = readFileSync(configPath, "utf-8");
|
|
91
727
|
const parsed = JSON.parse(content);
|
|
728
|
+
|
|
729
|
+
// Support both { servers: {...} } and direct server map
|
|
92
730
|
const servers = parsed.servers || parsed;
|
|
93
731
|
|
|
732
|
+
// Merge with defaults and filter to available
|
|
733
|
+
const merged: Record<string, ServerConfig> = { ...SERVERS };
|
|
734
|
+
|
|
735
|
+
for (const [name, config] of Object.entries(servers) as [string, Partial<ServerConfig>][]) {
|
|
736
|
+
if (merged[name]) {
|
|
737
|
+
// Merge with existing config
|
|
738
|
+
merged[name] = { ...merged[name], ...config };
|
|
739
|
+
} else {
|
|
740
|
+
// Add new server config
|
|
741
|
+
merged[name] = config as ServerConfig;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
94
745
|
// Filter to only enabled servers with available commands
|
|
95
746
|
const available: Record<string, ServerConfig> = {};
|
|
96
|
-
for (const [name, config] of Object.entries(
|
|
747
|
+
for (const [name, config] of Object.entries(merged)) {
|
|
97
748
|
if (config.disabled) continue;
|
|
98
|
-
|
|
99
|
-
|
|
749
|
+
const resolved = resolveCommand(config.command, cwd);
|
|
750
|
+
if (!resolved) continue;
|
|
751
|
+
available[name] = { ...config, resolvedCommand: resolved };
|
|
100
752
|
}
|
|
101
753
|
|
|
102
754
|
return { servers: available };
|
|
103
755
|
} catch {
|
|
104
|
-
// Ignore parse errors,
|
|
756
|
+
// Ignore parse errors, continue to next config or auto-detect
|
|
105
757
|
}
|
|
106
758
|
}
|
|
107
759
|
}
|
|
@@ -113,27 +765,49 @@ export function loadConfig(cwd: string): LspConfig {
|
|
|
113
765
|
// Check if project has root markers for this language
|
|
114
766
|
if (!hasRootMarkers(cwd, config.rootMarkers)) continue;
|
|
115
767
|
|
|
116
|
-
// Check if the language server binary is available
|
|
117
|
-
|
|
768
|
+
// Check if the language server binary is available (local or $PATH)
|
|
769
|
+
const resolved = resolveCommand(config.command, cwd);
|
|
770
|
+
if (!resolved) continue;
|
|
118
771
|
|
|
119
|
-
detected[name] = config;
|
|
772
|
+
detected[name] = { ...config, resolvedCommand: resolved };
|
|
120
773
|
}
|
|
121
774
|
|
|
122
775
|
return { servers: detected };
|
|
123
776
|
}
|
|
124
777
|
|
|
778
|
+
// =============================================================================
|
|
779
|
+
// Server Selection
|
|
780
|
+
// =============================================================================
|
|
781
|
+
|
|
125
782
|
/**
|
|
126
|
-
* Find
|
|
783
|
+
* Find all servers that can handle a file based on extension.
|
|
784
|
+
* Returns servers sorted with primary (non-linter) servers first.
|
|
127
785
|
*/
|
|
128
|
-
export function
|
|
786
|
+
export function getServersForFile(config: LspConfig, filePath: string): Array<[string, ServerConfig]> {
|
|
129
787
|
const ext = extname(filePath).toLowerCase();
|
|
788
|
+
const matches: Array<[string, ServerConfig]> = [];
|
|
130
789
|
|
|
131
790
|
for (const [name, serverConfig] of Object.entries(config.servers)) {
|
|
132
791
|
if (serverConfig.fileTypes.includes(ext)) {
|
|
133
|
-
|
|
792
|
+
matches.push([name, serverConfig]);
|
|
134
793
|
}
|
|
135
794
|
}
|
|
136
|
-
|
|
795
|
+
|
|
796
|
+
// Sort: primary servers (non-linters) first, then linters
|
|
797
|
+
return matches.sort((a, b) => {
|
|
798
|
+
const aIsLinter = a[1].isLinter ? 1 : 0;
|
|
799
|
+
const bIsLinter = b[1].isLinter ? 1 : 0;
|
|
800
|
+
return aIsLinter - bIsLinter;
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Find the primary server for a file (prefers type-checkers over linters).
|
|
806
|
+
* Used for operations like definition, hover, references that need type intelligence.
|
|
807
|
+
*/
|
|
808
|
+
export function getServerForFile(config: LspConfig, filePath: string): [string, ServerConfig] | null {
|
|
809
|
+
const servers = getServersForFile(config, filePath);
|
|
810
|
+
return servers.length > 0 ? servers[0] : null;
|
|
137
811
|
}
|
|
138
812
|
|
|
139
813
|
/**
|