@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.12

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.
Files changed (247) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
  3. package/dist/cli.js +10775 -10762
  4. package/dist/types/advisor/runtime.d.ts +1 -1
  5. package/dist/types/capability/mcp.d.ts +11 -0
  6. package/dist/types/capability/skill.d.ts +6 -0
  7. package/dist/types/cli/command-help.d.ts +3 -0
  8. package/dist/types/commands/share.d.ts +25 -0
  9. package/dist/types/commit/agentic/index.d.ts +3 -1
  10. package/dist/types/commit/execute.d.ts +32 -0
  11. package/dist/types/commit/index.d.ts +2 -1
  12. package/dist/types/commit/pipeline.d.ts +7 -1
  13. package/dist/types/config/custom-models.d.ts +31 -0
  14. package/dist/types/config/model-config-values.d.ts +19 -0
  15. package/dist/types/config/model-patch.d.ts +93 -0
  16. package/dist/types/config/model-provider-discovery.d.ts +51 -0
  17. package/dist/types/config/model-registry.d.ts +9 -52
  18. package/dist/types/config/settings-schema.d.ts +1 -34
  19. package/dist/types/config/settings.d.ts +5 -3
  20. package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
  21. package/dist/types/discovery/agent-plugin-format.d.ts +136 -0
  22. package/dist/types/discovery/agent-plugins.d.ts +1 -0
  23. package/dist/types/discovery/contained-path.d.ts +33 -0
  24. package/dist/types/discovery/index.d.ts +1 -0
  25. package/dist/types/eval/executor-base.d.ts +8 -2
  26. package/dist/types/eval/kernel-session-registry.d.ts +60 -0
  27. package/dist/types/export/share.d.ts +1 -1
  28. package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
  29. package/dist/types/extensibility/extensions/loader.d.ts +7 -0
  30. package/dist/types/extensibility/extensions/types.d.ts +11 -0
  31. package/dist/types/extensibility/shared-events.d.ts +3 -2
  32. package/dist/types/extensibility/skills.d.ts +5 -0
  33. package/dist/types/lsp/diagnostics.d.ts +96 -0
  34. package/dist/types/lsp/index.d.ts +7 -128
  35. package/dist/types/lsp/servers.d.ts +72 -0
  36. package/dist/types/lsp/tool.d.ts +42 -0
  37. package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
  38. package/dist/types/lsp/writethrough.d.ts +33 -0
  39. package/dist/types/mcp/transports/header-policy.d.ts +46 -0
  40. package/dist/types/mcp/types.d.ts +15 -0
  41. package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
  42. package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
  43. package/dist/types/modes/components/agent-hub.d.ts +2 -0
  44. package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
  45. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  46. package/dist/types/modes/components/custom-editor.d.ts +1 -2
  47. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  48. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  49. package/dist/types/modes/theme/color.d.ts +19 -0
  50. package/dist/types/modes/theme/loader.d.ts +19 -0
  51. package/dist/types/modes/theme/schema.d.ts +175 -0
  52. package/dist/types/modes/theme/symbols.d.ts +28 -0
  53. package/dist/types/modes/theme/theme-class.d.ts +244 -0
  54. package/dist/types/modes/theme/theme.d.ts +9 -280
  55. package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
  56. package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
  57. package/dist/types/registry/agent-registry.d.ts +1 -3
  58. package/dist/types/secrets/index.d.ts +26 -2
  59. package/dist/types/secrets/message-transform.d.ts +40 -0
  60. package/dist/types/secrets/obfuscator.d.ts +0 -108
  61. package/dist/types/secrets/placeholder-scan.d.ts +95 -0
  62. package/dist/types/secrets/placeholder.d.ts +94 -0
  63. package/dist/types/secrets/replacement.d.ts +82 -0
  64. package/dist/types/session/agent-session-events.d.ts +2 -2
  65. package/dist/types/session/agent-session-types.d.ts +6 -0
  66. package/dist/types/session/agent-session.d.ts +2 -2
  67. package/dist/types/session/messages.d.ts +1 -0
  68. package/dist/types/session/prewalk.d.ts +3 -1
  69. package/dist/types/session/session-handoff.d.ts +3 -3
  70. package/dist/types/session/session-manager.d.ts +32 -0
  71. package/dist/types/session/session-provider-boundary.d.ts +1 -1
  72. package/dist/types/session/session-tools.d.ts +8 -0
  73. package/dist/types/session/turn-recovery.d.ts +9 -4
  74. package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
  75. package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
  76. package/dist/types/slash-commands/builtin-control.d.ts +2 -0
  77. package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
  78. package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
  79. package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
  80. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  81. package/dist/types/slash-commands/builtin-session.d.ts +2 -0
  82. package/dist/types/task/index.d.ts +2 -0
  83. package/dist/types/tools/gh-common.d.ts +69 -0
  84. package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
  85. package/dist/types/tools/gh-pr-diff.d.ts +102 -0
  86. package/dist/types/tools/gh-run-watch.d.ts +78 -0
  87. package/dist/types/tools/gh-search.d.ts +61 -0
  88. package/dist/types/tools/gh-types.d.ts +340 -0
  89. package/dist/types/tools/gh-view.d.ts +88 -0
  90. package/dist/types/tools/gh.d.ts +4 -201
  91. package/dist/types/tools/read-archive.d.ts +16 -0
  92. package/dist/types/tools/read-format.d.ts +104 -0
  93. package/dist/types/tools/read-path-resolution.d.ts +19 -0
  94. package/dist/types/tools/read-pdf-images.d.ts +12 -0
  95. package/dist/types/tools/read-renderer.d.ts +24 -0
  96. package/dist/types/tools/read-selector.d.ts +27 -0
  97. package/dist/types/tools/read-sqlite.d.ts +16 -0
  98. package/dist/types/tools/read-summary.d.ts +19 -0
  99. package/dist/types/tools/read.d.ts +1 -23
  100. package/dist/types/tools/shell-tokenize.d.ts +18 -1
  101. package/dist/types/utils/changelog.d.ts +0 -5
  102. package/package.json +13 -13
  103. package/scripts/legacy-pi-virtual-module.ts +4 -1
  104. package/src/advisor/runtime.ts +12 -7
  105. package/src/capability/mcp.ts +11 -0
  106. package/src/capability/skill.ts +6 -0
  107. package/src/cli/command-help.ts +4 -0
  108. package/src/cli/gallery-cli.ts +1 -1
  109. package/src/cli-commands.ts +5 -0
  110. package/src/commands/commit.ts +16 -3
  111. package/src/commands/share.ts +71 -0
  112. package/src/commit/agentic/index.ts +39 -21
  113. package/src/commit/execute.ts +56 -0
  114. package/src/commit/index.ts +2 -1
  115. package/src/commit/pipeline.ts +20 -7
  116. package/src/config/custom-models.ts +188 -0
  117. package/src/config/model-config-values.ts +128 -0
  118. package/src/config/model-discovery.ts +1 -1
  119. package/src/config/model-patch.ts +252 -0
  120. package/src/config/model-provider-discovery.ts +132 -0
  121. package/src/config/model-registry.ts +84 -704
  122. package/src/config/settings-schema.ts +2 -33
  123. package/src/config/settings.ts +53 -3
  124. package/src/debug/raw-sse-buffer.ts +20 -0
  125. package/src/discovery/agent-plugin-format.ts +551 -0
  126. package/src/discovery/agent-plugins.ts +341 -0
  127. package/src/discovery/claude-plugins.ts +21 -5
  128. package/src/discovery/contained-path.ts +75 -0
  129. package/src/discovery/helpers.ts +23 -9
  130. package/src/discovery/index.ts +1 -0
  131. package/src/discovery/omp-plugins.ts +20 -7
  132. package/src/edit/hashline/diff.ts +5 -1
  133. package/src/eval/executor-base.ts +39 -6
  134. package/src/eval/jl/executor.ts +82 -371
  135. package/src/eval/kernel-session-registry.ts +398 -0
  136. package/src/eval/py/executor.ts +53 -261
  137. package/src/eval/rb/executor.ts +36 -279
  138. package/src/exec/non-interactive-env.ts +1 -0
  139. package/src/export/share.ts +2 -1
  140. package/src/extensibility/custom-tools/types.ts +2 -2
  141. package/src/extensibility/extensions/loader.ts +78 -14
  142. package/src/extensibility/extensions/runner.ts +6 -0
  143. package/src/extensibility/extensions/types.ts +12 -0
  144. package/src/extensibility/plugins/marketplace/manager.ts +2 -1
  145. package/src/extensibility/shared-events.ts +3 -2
  146. package/src/extensibility/skills.ts +9 -0
  147. package/src/internal-urls/skill-protocol.ts +14 -0
  148. package/src/internal-urls/vault-protocol.ts +2 -2
  149. package/src/launch/client.ts +11 -1
  150. package/src/launch/protocol.ts +7 -2
  151. package/src/lsp/diagnostics.ts +516 -0
  152. package/src/lsp/index.ts +20 -2819
  153. package/src/lsp/servers.ts +296 -0
  154. package/src/lsp/tool.ts +1352 -0
  155. package/src/lsp/workspace-diagnostics.ts +170 -0
  156. package/src/lsp/writethrough.ts +561 -0
  157. package/src/main.ts +3 -2
  158. package/src/mcp/config.ts +3 -0
  159. package/src/mcp/manager.ts +12 -9
  160. package/src/mcp/transports/header-policy.ts +95 -0
  161. package/src/mcp/transports/http.ts +35 -52
  162. package/src/mcp/transports/sse.ts +20 -27
  163. package/src/mcp/types.ts +15 -0
  164. package/src/modes/acp/acp-agent.ts +15 -4
  165. package/src/modes/components/agent-dashboard.ts +2 -0
  166. package/src/modes/components/agent-hub-projection.ts +2 -2
  167. package/src/modes/components/agent-hub-renderer.ts +3 -7
  168. package/src/modes/components/agent-hub.ts +5 -0
  169. package/src/modes/components/agent-transcript-viewer.ts +3 -0
  170. package/src/modes/components/chat-transcript-builder.ts +3 -0
  171. package/src/modes/components/custom-editor.ts +0 -9
  172. package/src/modes/components/status-line/component.ts +1 -0
  173. package/src/modes/components/status-line/segments.ts +6 -4
  174. package/src/modes/components/status-line/types.ts +2 -0
  175. package/src/modes/components/tool-execution.ts +13 -16
  176. package/src/modes/components/tree-selector.ts +62 -3
  177. package/src/modes/controllers/command-controller.ts +8 -2
  178. package/src/modes/controllers/event-controller.ts +15 -15
  179. package/src/modes/controllers/input-controller.ts +20 -2
  180. package/src/modes/controllers/selector-controller.ts +1 -0
  181. package/src/modes/theme/color.ts +133 -0
  182. package/src/modes/theme/loader.ts +178 -0
  183. package/src/modes/theme/schema.ts +263 -0
  184. package/src/modes/theme/symbols.ts +1000 -0
  185. package/src/modes/theme/theme-class.ts +611 -0
  186. package/src/modes/theme/theme.ts +27 -2453
  187. package/src/modes/theme/tui-adapters.ts +279 -0
  188. package/src/modes/utils/transcript-render-helpers.ts +4 -2
  189. package/src/modes/utils/ui-helpers.ts +1 -0
  190. package/src/registry/agent-registry.ts +2 -2
  191. package/src/sdk.ts +6 -49
  192. package/src/secrets/index.ts +51 -6
  193. package/src/secrets/message-transform.ts +287 -0
  194. package/src/secrets/obfuscator.ts +39 -1303
  195. package/src/secrets/placeholder-scan.ts +506 -0
  196. package/src/secrets/placeholder.ts +309 -0
  197. package/src/secrets/replacement.ts +216 -0
  198. package/src/session/agent-session-events.ts +2 -2
  199. package/src/session/agent-session-types.ts +6 -0
  200. package/src/session/agent-session.ts +146 -21
  201. package/src/session/indexed-session-storage.ts +7 -2
  202. package/src/session/messages.ts +1 -0
  203. package/src/session/prewalk.ts +57 -17
  204. package/src/session/session-advisors.ts +32 -34
  205. package/src/session/session-context.ts +3 -5
  206. package/src/session/session-handoff.ts +39 -16
  207. package/src/session/session-manager.ts +67 -3
  208. package/src/session/session-provider-boundary.ts +3 -6
  209. package/src/session/session-tools.ts +37 -3
  210. package/src/session/todo-tracker.ts +11 -1
  211. package/src/session/turn-recovery.ts +115 -84
  212. package/src/slash-commands/builtin-collaboration.ts +504 -0
  213. package/src/slash-commands/builtin-completions.ts +291 -0
  214. package/src/slash-commands/builtin-control.ts +78 -0
  215. package/src/slash-commands/builtin-lifecycle.ts +506 -0
  216. package/src/slash-commands/builtin-marketplace.ts +567 -0
  217. package/src/slash-commands/builtin-modes.ts +518 -0
  218. package/src/slash-commands/builtin-registry.ts +21 -2996
  219. package/src/slash-commands/builtin-session.ts +592 -0
  220. package/src/task/executor.ts +17 -14
  221. package/src/task/index.ts +21 -6
  222. package/src/tools/bash-skill-urls.ts +15 -0
  223. package/src/tools/bash.ts +10 -11
  224. package/src/tools/debug.ts +1 -1
  225. package/src/tools/gh-common.ts +288 -0
  226. package/src/tools/gh-pr-checkout.ts +679 -0
  227. package/src/tools/gh-pr-diff.ts +473 -0
  228. package/src/tools/gh-run-watch.ts +1015 -0
  229. package/src/tools/gh-search.ts +500 -0
  230. package/src/tools/gh-types.ts +379 -0
  231. package/src/tools/gh-view.ts +612 -0
  232. package/src/tools/gh.ts +109 -3821
  233. package/src/tools/jtd-to-json-schema.ts +123 -21
  234. package/src/tools/read-archive.ts +209 -0
  235. package/src/tools/read-format.ts +593 -0
  236. package/src/tools/read-path-resolution.ts +36 -0
  237. package/src/tools/read-pdf-images.ts +250 -0
  238. package/src/tools/read-renderer.ts +289 -0
  239. package/src/tools/read-selector.ts +84 -0
  240. package/src/tools/read-sqlite.ts +215 -0
  241. package/src/tools/read-summary.ts +199 -0
  242. package/src/tools/read.ts +82 -1824
  243. package/src/tools/shell-tokenize.ts +99 -1
  244. package/src/utils/changelog.ts +1 -1
  245. package/src/utils/title-generator.ts +3 -1
  246. package/src/web/search/providers/exa.ts +1 -1
  247. package/src/web/search/providers/zai.ts +12 -3
@@ -0,0 +1,296 @@
1
+ import { logger } from "@oh-my-pi/pi-utils";
2
+ import { throwIfAborted } from "../tools/tool-errors";
3
+ import {
4
+ getActiveClients,
5
+ getActiveOrPendingClient,
6
+ getOrCreateClient,
7
+ type LspServerStatus,
8
+ notifySaved,
9
+ sendNotification,
10
+ sendRequest,
11
+ setIdleTimeout,
12
+ shutdownClientInstance,
13
+ syncContent,
14
+ WARMUP_TIMEOUT_MS,
15
+ } from "./client";
16
+ import { getServersForFile, type LspConfig, loadConfig } from "./config";
17
+ import { MUX_RESTART_METHOD } from "./mux/protocol";
18
+ import type { LspClient, ServerConfig } from "./types";
19
+
20
+ /**
21
+ * LSP actions that do not mutate the workspace or language-server state.
22
+ * Anything not in this set (rename, code_actions with apply, rename_file,
23
+ * reload, raw request, etc.) is classified as write-tier.
24
+ */
25
+ export const LSP_READONLY_ACTIONS: ReadonlySet<string> = new Set([
26
+ "diagnostics",
27
+ "definition",
28
+ "type_definition",
29
+ "implementation",
30
+ "references",
31
+ "hover",
32
+ "symbols",
33
+ "status",
34
+ "capabilities",
35
+ ]);
36
+
37
+ export interface LspStartupServerInfo {
38
+ name: string;
39
+ status: "connecting" | "ready" | "error" | "available";
40
+ fileTypes: string[];
41
+ error?: string;
42
+ }
43
+
44
+ /** Result from warming up LSP servers */
45
+ export interface LspWarmupResult {
46
+ servers: Array<LspStartupServerInfo & { status: "ready" | "error" }>;
47
+ }
48
+
49
+ /** Options for warming up LSP servers */
50
+ export interface LspWarmupOptions {
51
+ /** Called when starting to connect to servers */
52
+ onConnecting?: (serverNames: string[]) => void;
53
+ }
54
+
55
+ export function discoverStartupLspServers(
56
+ cwd: string,
57
+ status: LspStartupServerInfo["status"] = "connecting",
58
+ ): LspStartupServerInfo[] {
59
+ const config = loadConfig(cwd);
60
+ return getLspServers(config).map(([name, serverConfig]) => ({
61
+ name,
62
+ status,
63
+ fileTypes: serverConfig.fileTypes,
64
+ }));
65
+ }
66
+
67
+ /**
68
+ * Warm up LSP servers for a directory by connecting to all detected servers.
69
+ * This should be called at startup to avoid cold-start delays.
70
+ *
71
+ * @param cwd - Working directory to detect and start servers for
72
+ * @param options - Optional callbacks for progress reporting
73
+ * @returns Status of each server that was started
74
+ */
75
+ export async function warmupLspServers(cwd: string, options?: LspWarmupOptions): Promise<LspWarmupResult> {
76
+ const config = loadConfig(cwd);
77
+ setIdleTimeout(config.idleTimeoutMs);
78
+ const servers: LspWarmupResult["servers"] = [];
79
+ const lspServers = getLspServers(config);
80
+
81
+ // Notify caller which servers we're connecting to
82
+ if (lspServers.length > 0 && options?.onConnecting) {
83
+ options.onConnecting(lspServers.map(([name]) => name));
84
+ }
85
+
86
+ // Start all detected servers in parallel with a short timeout
87
+ // Servers that don't respond quickly will be initialized lazily on first use
88
+ const results = await Promise.allSettled(
89
+ lspServers.map(async ([name, serverConfig]) => {
90
+ const client = await getOrCreateClient(serverConfig, cwd, serverConfig.warmupTimeoutMs ?? WARMUP_TIMEOUT_MS);
91
+ return { name, client, fileTypes: serverConfig.fileTypes };
92
+ }),
93
+ );
94
+
95
+ for (let i = 0; i < results.length; i++) {
96
+ const result = results[i];
97
+ const [name, serverConfig] = lspServers[i];
98
+ if (result.status === "fulfilled") {
99
+ servers.push({
100
+ name: result.value.name,
101
+ status: "ready",
102
+ fileTypes: result.value.fileTypes,
103
+ });
104
+ } else {
105
+ const errorMsg = result.reason?.message ?? String(result.reason);
106
+ logger.warn("LSP server failed to start", { server: name, error: errorMsg });
107
+ servers.push({
108
+ name,
109
+ status: "error",
110
+ fileTypes: serverConfig.fileTypes,
111
+ error: errorMsg,
112
+ });
113
+ }
114
+ }
115
+
116
+ return { servers };
117
+ }
118
+
119
+ /**
120
+ * Get status of currently active LSP servers.
121
+ */
122
+ export function getLspStatus(): LspServerStatus[] {
123
+ return getActiveClients();
124
+ }
125
+
126
+ /**
127
+ * Sync in-memory file content to all applicable LSP servers.
128
+ * Sends didOpen (if new) or didChange (if already open).
129
+ *
130
+ * @param absolutePath - Absolute path to the file
131
+ * @param content - The new file content
132
+ * @param cwd - Working directory for LSP config resolution
133
+ * @param servers - Servers to sync to
134
+ */
135
+ export async function syncFileContent(
136
+ absolutePath: string,
137
+ content: string,
138
+ cwd: string,
139
+ servers: Array<[string, ServerConfig]>,
140
+ signal?: AbortSignal,
141
+ createMissing = true,
142
+ ): Promise<void> {
143
+ throwIfAborted(signal);
144
+ await Promise.allSettled(
145
+ servers.map(async ([_serverName, serverConfig]) => {
146
+ throwIfAborted(signal);
147
+ if (serverConfig.createClient) {
148
+ return;
149
+ }
150
+ const client = createMissing
151
+ ? await getOrCreateClient(serverConfig, cwd, undefined, signal)
152
+ : await getActiveOrPendingClient(serverConfig, cwd, signal);
153
+ if (!client) return;
154
+ throwIfAborted(signal);
155
+ await syncContent(client, absolutePath, content, signal);
156
+ }),
157
+ );
158
+ throwIfAborted(signal);
159
+ }
160
+
161
+ /**
162
+ * Notify all LSP servers that a file was saved.
163
+ * Assumes content was already synced via syncFileContent.
164
+ *
165
+ * @param absolutePath - Absolute path to the file
166
+ * @param cwd - Working directory for LSP config resolution
167
+ * @param servers - Servers to notify
168
+ */
169
+ export async function notifyFileSaved(
170
+ absolutePath: string,
171
+ cwd: string,
172
+ servers: Array<[string, ServerConfig]>,
173
+ signal?: AbortSignal,
174
+ createMissing = true,
175
+ ): Promise<void> {
176
+ throwIfAborted(signal);
177
+ await Promise.allSettled(
178
+ servers.map(async ([_serverName, serverConfig]) => {
179
+ throwIfAborted(signal);
180
+ if (serverConfig.createClient) {
181
+ return;
182
+ }
183
+ const client = createMissing
184
+ ? await getOrCreateClient(serverConfig, cwd, undefined, signal)
185
+ : await getActiveOrPendingClient(serverConfig, cwd, signal);
186
+ if (!client) return;
187
+ await notifySaved(client, absolutePath, signal);
188
+ }),
189
+ );
190
+ throwIfAborted(signal);
191
+ }
192
+
193
+ // Cache config per cwd to avoid repeated file I/O
194
+ export const configCache = new Map<string, LspConfig>();
195
+
196
+ export function getConfig(cwd: string): LspConfig {
197
+ let config = configCache.get(cwd);
198
+ if (!config) {
199
+ config = loadConfig(cwd);
200
+ setIdleTimeout(config.idleTimeoutMs);
201
+ configCache.set(cwd, config);
202
+ }
203
+ return config;
204
+ }
205
+
206
+ function isCustomLinter(serverConfig: ServerConfig): boolean {
207
+ return Boolean(serverConfig.createClient);
208
+ }
209
+
210
+ export function splitServers(servers: Array<[string, ServerConfig]>): {
211
+ lspServers: Array<[string, ServerConfig]>;
212
+ customLinterServers: Array<[string, ServerConfig]>;
213
+ } {
214
+ const lspServers: Array<[string, ServerConfig]> = [];
215
+ const customLinterServers: Array<[string, ServerConfig]> = [];
216
+ for (const entry of servers) {
217
+ if (isCustomLinter(entry[1])) {
218
+ customLinterServers.push(entry);
219
+ } else {
220
+ lspServers.push(entry);
221
+ }
222
+ }
223
+ return { lspServers, customLinterServers };
224
+ }
225
+
226
+ export function getLspServers(config: LspConfig): Array<[string, ServerConfig]> {
227
+ return (Object.entries(config.servers) as Array<[string, ServerConfig]>).filter(
228
+ ([, serverConfig]) => !isCustomLinter(serverConfig),
229
+ );
230
+ }
231
+
232
+ export function getLspServersForFile(config: LspConfig, filePath: string): Array<[string, ServerConfig]> {
233
+ return getServersForFile(config, filePath).filter(([, serverConfig]) => !isCustomLinter(serverConfig));
234
+ }
235
+
236
+ export function getLspServerForFile(config: LspConfig, filePath: string): [string, ServerConfig] | null {
237
+ const servers = getLspServersForFile(config, filePath);
238
+ return servers.length > 0 ? servers[0] : null;
239
+ }
240
+
241
+ export function isProjectAwareLspServer(serverConfig: ServerConfig): boolean {
242
+ return !serverConfig.createClient && !serverConfig.isLinter;
243
+ }
244
+
245
+ /** True when an LSP error indicates the server doesn't implement the requested method. */
246
+ export function isMethodNotFoundError(err: unknown): boolean {
247
+ if (!(err instanceof Error)) return false;
248
+ const msg = err.message.toLowerCase();
249
+ return (
250
+ msg.includes("method not found") ||
251
+ msg.includes("unhandled method") ||
252
+ msg.includes("not supported") ||
253
+ msg.includes("-32601")
254
+ );
255
+ }
256
+
257
+ export async function reloadServer(client: LspClient, serverName: string, signal?: AbortSignal): Promise<string> {
258
+ throwIfAborted(signal);
259
+ // rust-analyzer exposes a real reload request. Every other server rejects it
260
+ // with method-not-found — that alone justifies the generic fallback. A caller
261
+ // cancel or tool timeout must propagate, never be mistaken for an unsupported
262
+ // method and swallowed into a bogus "Restarted" (issue #6369).
263
+ try {
264
+ await sendRequest(client, "rust-analyzer/reloadWorkspace", null, signal);
265
+ return `Reloaded ${serverName}`;
266
+ } catch (err) {
267
+ throwIfAborted(signal);
268
+ if (!isMethodNotFoundError(err)) throw err;
269
+ // Method not supported — fall through to the generic reload.
270
+ }
271
+ // workspace/didChangeConfiguration is a notification per spec; sending it
272
+ // as a request hangs until the tool deadline on servers that route it to
273
+ // the notification handler and never respond.
274
+ try {
275
+ await sendNotification(client, "workspace/didChangeConfiguration", { settings: {} }, signal);
276
+ return `Reloaded ${serverName}`;
277
+ } catch {
278
+ throwIfAborted(signal);
279
+ // The reload notification could not be delivered — the connection is
280
+ // wedged or the process already died. Tear the client down (removing it
281
+ // from the registry by identity and awaiting confirmed process exit) so
282
+ // the next request cold-starts a fresh client. A kill that never confirms
283
+ // exit is not a restart: surface the teardown failure truthfully.
284
+ //
285
+ // On a broker-shared link a per-session teardown only detaches this
286
+ // process while the wedged server keeps serving everyone else — ask the
287
+ // mux to kill the shared server first (best-effort; it also severs us).
288
+ if (client.proc.sharedMux) {
289
+ await sendNotification(client, MUX_RESTART_METHOD, undefined, AbortSignal.timeout(2_000)).catch(() => {});
290
+ }
291
+ if (!(await shutdownClientInstance(client))) {
292
+ throw new Error(`Failed to restart ${serverName}: server process did not exit after kill`);
293
+ }
294
+ return `Restarted ${serverName}`;
295
+ }
296
+ }