@nogataka/smart-edit 0.0.14

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 (186) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +244 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +7 -0
  5. package/dist/devtools/generate_prompt_factory.d.ts +5 -0
  6. package/dist/devtools/generate_prompt_factory.js +114 -0
  7. package/dist/index.d.ts +34 -0
  8. package/dist/index.js +34 -0
  9. package/dist/interprompt/index.d.ts +2 -0
  10. package/dist/interprompt/index.js +1 -0
  11. package/dist/interprompt/jinja_template.d.ts +10 -0
  12. package/dist/interprompt/jinja_template.js +174 -0
  13. package/dist/interprompt/multilang_prompt.d.ts +54 -0
  14. package/dist/interprompt/multilang_prompt.js +302 -0
  15. package/dist/interprompt/prompt_factory.d.ts +16 -0
  16. package/dist/interprompt/prompt_factory.js +189 -0
  17. package/dist/interprompt/util/class_decorators.d.ts +1 -0
  18. package/dist/interprompt/util/class_decorators.js +1 -0
  19. package/dist/interprompt/util/index.d.ts +1 -0
  20. package/dist/interprompt/util/index.js +1 -0
  21. package/dist/serena/agent.d.ts +118 -0
  22. package/dist/serena/agent.js +675 -0
  23. package/dist/serena/agno.d.ts +111 -0
  24. package/dist/serena/agno.js +278 -0
  25. package/dist/serena/analytics.d.ts +24 -0
  26. package/dist/serena/analytics.js +119 -0
  27. package/dist/serena/cli.d.ts +9 -0
  28. package/dist/serena/cli.js +731 -0
  29. package/dist/serena/code_editor.d.ts +42 -0
  30. package/dist/serena/code_editor.js +239 -0
  31. package/dist/serena/config/context_mode.d.ts +41 -0
  32. package/dist/serena/config/context_mode.js +239 -0
  33. package/dist/serena/config/serena_config.d.ts +134 -0
  34. package/dist/serena/config/serena_config.js +718 -0
  35. package/dist/serena/constants.d.ts +18 -0
  36. package/dist/serena/constants.js +27 -0
  37. package/dist/serena/dashboard.d.ts +55 -0
  38. package/dist/serena/dashboard.js +472 -0
  39. package/dist/serena/generated/generated_prompt_factory.d.ts +27 -0
  40. package/dist/serena/generated/generated_prompt_factory.js +42 -0
  41. package/dist/serena/gui_log_viewer.d.ts +41 -0
  42. package/dist/serena/gui_log_viewer.js +436 -0
  43. package/dist/serena/mcp.d.ts +118 -0
  44. package/dist/serena/mcp.js +904 -0
  45. package/dist/serena/project.d.ts +62 -0
  46. package/dist/serena/project.js +321 -0
  47. package/dist/serena/prompt_factory.d.ts +20 -0
  48. package/dist/serena/prompt_factory.js +42 -0
  49. package/dist/serena/resources/config/contexts/agent.yml +8 -0
  50. package/dist/serena/resources/config/contexts/chatgpt.yml +28 -0
  51. package/dist/serena/resources/config/contexts/codex.yml +27 -0
  52. package/dist/serena/resources/config/contexts/context.template.yml +11 -0
  53. package/dist/serena/resources/config/contexts/desktop-app.yml +17 -0
  54. package/dist/serena/resources/config/contexts/ide-assistant.yml +26 -0
  55. package/dist/serena/resources/config/contexts/oaicompat-agent.yml +8 -0
  56. package/dist/serena/resources/config/internal_modes/jetbrains.yml +15 -0
  57. package/dist/serena/resources/config/modes/editing.yml +112 -0
  58. package/dist/serena/resources/config/modes/interactive.yml +11 -0
  59. package/dist/serena/resources/config/modes/mode.template.yml +7 -0
  60. package/dist/serena/resources/config/modes/no-onboarding.yml +8 -0
  61. package/dist/serena/resources/config/modes/onboarding.yml +16 -0
  62. package/dist/serena/resources/config/modes/one-shot.yml +15 -0
  63. package/dist/serena/resources/config/modes/planning.yml +15 -0
  64. package/dist/serena/resources/config/prompt_templates/simple_tool_outputs.yml +75 -0
  65. package/dist/serena/resources/config/prompt_templates/system_prompt.yml +66 -0
  66. package/dist/serena/resources/dashboard/dashboard.js +815 -0
  67. package/dist/serena/resources/dashboard/index.html +314 -0
  68. package/dist/serena/resources/dashboard/jquery.min.js +3 -0
  69. package/dist/serena/resources/dashboard/serena-icon-16.png +0 -0
  70. package/dist/serena/resources/dashboard/serena-icon-32.png +0 -0
  71. package/dist/serena/resources/dashboard/serena-icon-48.png +0 -0
  72. package/dist/serena/resources/dashboard/serena-logs-dark-mode.png +0 -0
  73. package/dist/serena/resources/dashboard/serena-logs.png +0 -0
  74. package/dist/serena/resources/project.template.yml +67 -0
  75. package/dist/serena/resources/serena_config.template.yml +85 -0
  76. package/dist/serena/symbol.d.ts +199 -0
  77. package/dist/serena/symbol.js +616 -0
  78. package/dist/serena/text_utils.d.ts +51 -0
  79. package/dist/serena/text_utils.js +267 -0
  80. package/dist/serena/tools/cmd_tools.d.ts +31 -0
  81. package/dist/serena/tools/cmd_tools.js +48 -0
  82. package/dist/serena/tools/config_tools.d.ts +53 -0
  83. package/dist/serena/tools/config_tools.js +176 -0
  84. package/dist/serena/tools/file_tools.d.ts +231 -0
  85. package/dist/serena/tools/file_tools.js +511 -0
  86. package/dist/serena/tools/index.d.ts +7 -0
  87. package/dist/serena/tools/index.js +7 -0
  88. package/dist/serena/tools/memory_tools.d.ts +60 -0
  89. package/dist/serena/tools/memory_tools.js +135 -0
  90. package/dist/serena/tools/symbol_tools.d.ts +165 -0
  91. package/dist/serena/tools/symbol_tools.js +362 -0
  92. package/dist/serena/tools/tools_base.d.ts +162 -0
  93. package/dist/serena/tools/tools_base.js +378 -0
  94. package/dist/serena/tools/workflow_tools.d.ts +35 -0
  95. package/dist/serena/tools/workflow_tools.js +161 -0
  96. package/dist/serena/util/class_decorators.d.ts +7 -0
  97. package/dist/serena/util/class_decorators.js +37 -0
  98. package/dist/serena/util/exception.d.ts +8 -0
  99. package/dist/serena/util/exception.js +53 -0
  100. package/dist/serena/util/file_system.d.ts +30 -0
  101. package/dist/serena/util/file_system.js +352 -0
  102. package/dist/serena/util/general.d.ts +11 -0
  103. package/dist/serena/util/general.js +42 -0
  104. package/dist/serena/util/git.d.ts +11 -0
  105. package/dist/serena/util/git.js +37 -0
  106. package/dist/serena/util/inspection.d.ts +45 -0
  107. package/dist/serena/util/inspection.js +221 -0
  108. package/dist/serena/util/logging.d.ts +46 -0
  109. package/dist/serena/util/logging.js +205 -0
  110. package/dist/serena/util/shell.d.ts +21 -0
  111. package/dist/serena/util/shell.js +95 -0
  112. package/dist/serena/util/thread.d.ts +23 -0
  113. package/dist/serena/util/thread.js +88 -0
  114. package/dist/serena/version.d.ts +1 -0
  115. package/dist/serena/version.js +23 -0
  116. package/dist/solidlsp/language_servers/autoload.d.ts +23 -0
  117. package/dist/solidlsp/language_servers/autoload.js +25 -0
  118. package/dist/solidlsp/language_servers/bash_language_server.d.ts +10 -0
  119. package/dist/solidlsp/language_servers/bash_language_server.js +64 -0
  120. package/dist/solidlsp/language_servers/clangd_language_server.d.ts +13 -0
  121. package/dist/solidlsp/language_servers/clangd_language_server.js +110 -0
  122. package/dist/solidlsp/language_servers/clojure_lsp.d.ts +13 -0
  123. package/dist/solidlsp/language_servers/clojure_lsp.js +137 -0
  124. package/dist/solidlsp/language_servers/common.d.ts +41 -0
  125. package/dist/solidlsp/language_servers/common.js +365 -0
  126. package/dist/solidlsp/language_servers/csharp_language_server.d.ts +21 -0
  127. package/dist/solidlsp/language_servers/csharp_language_server.js +694 -0
  128. package/dist/solidlsp/language_servers/dart_language_server.d.ts +10 -0
  129. package/dist/solidlsp/language_servers/dart_language_server.js +122 -0
  130. package/dist/solidlsp/language_servers/eclipse_jdtls.d.ts +24 -0
  131. package/dist/solidlsp/language_servers/eclipse_jdtls.js +671 -0
  132. package/dist/solidlsp/language_servers/erlang_language_server.d.ts +22 -0
  133. package/dist/solidlsp/language_servers/erlang_language_server.js +327 -0
  134. package/dist/solidlsp/language_servers/gopls.d.ts +12 -0
  135. package/dist/solidlsp/language_servers/gopls.js +59 -0
  136. package/dist/solidlsp/language_servers/intelephense.d.ts +13 -0
  137. package/dist/solidlsp/language_servers/intelephense.js +121 -0
  138. package/dist/solidlsp/language_servers/jedi_server.d.ts +18 -0
  139. package/dist/solidlsp/language_servers/jedi_server.js +234 -0
  140. package/dist/solidlsp/language_servers/kotlin_language_server.d.ts +19 -0
  141. package/dist/solidlsp/language_servers/kotlin_language_server.js +474 -0
  142. package/dist/solidlsp/language_servers/lua_ls.d.ts +18 -0
  143. package/dist/solidlsp/language_servers/lua_ls.js +319 -0
  144. package/dist/solidlsp/language_servers/nixd_language_server.d.ts +17 -0
  145. package/dist/solidlsp/language_servers/nixd_language_server.js +341 -0
  146. package/dist/solidlsp/language_servers/pyright_server.d.ts +19 -0
  147. package/dist/solidlsp/language_servers/pyright_server.js +180 -0
  148. package/dist/solidlsp/language_servers/r_language_server.d.ts +19 -0
  149. package/dist/solidlsp/language_servers/r_language_server.js +184 -0
  150. package/dist/solidlsp/language_servers/ruby_common.d.ts +10 -0
  151. package/dist/solidlsp/language_servers/ruby_common.js +136 -0
  152. package/dist/solidlsp/language_servers/ruby_lsp.d.ts +18 -0
  153. package/dist/solidlsp/language_servers/ruby_lsp.js +230 -0
  154. package/dist/solidlsp/language_servers/rust_analyzer.d.ts +13 -0
  155. package/dist/solidlsp/language_servers/rust_analyzer.js +96 -0
  156. package/dist/solidlsp/language_servers/solargraph.d.ts +18 -0
  157. package/dist/solidlsp/language_servers/solargraph.js +208 -0
  158. package/dist/solidlsp/language_servers/sourcekit_lsp.d.ts +24 -0
  159. package/dist/solidlsp/language_servers/sourcekit_lsp.js +449 -0
  160. package/dist/solidlsp/language_servers/terraform_ls.d.ts +13 -0
  161. package/dist/solidlsp/language_servers/terraform_ls.js +139 -0
  162. package/dist/solidlsp/language_servers/typescript_language_server.d.ts +20 -0
  163. package/dist/solidlsp/language_servers/typescript_language_server.js +237 -0
  164. package/dist/solidlsp/language_servers/vts_language_server.d.ts +13 -0
  165. package/dist/solidlsp/language_servers/vts_language_server.js +121 -0
  166. package/dist/solidlsp/language_servers/zls.d.ts +20 -0
  167. package/dist/solidlsp/language_servers/zls.js +254 -0
  168. package/dist/solidlsp/ls.d.ts +197 -0
  169. package/dist/solidlsp/ls.js +507 -0
  170. package/dist/solidlsp/ls_config.d.ts +43 -0
  171. package/dist/solidlsp/ls_config.js +157 -0
  172. package/dist/solidlsp/ls_exceptions.d.ts +5 -0
  173. package/dist/solidlsp/ls_exceptions.js +14 -0
  174. package/dist/solidlsp/ls_handler.d.ts +54 -0
  175. package/dist/solidlsp/ls_handler.js +406 -0
  176. package/dist/solidlsp/ls_request.d.ts +31 -0
  177. package/dist/solidlsp/ls_request.js +42 -0
  178. package/dist/solidlsp/ls_types.d.ts +7 -0
  179. package/dist/solidlsp/ls_types.js +8 -0
  180. package/dist/solidlsp/lsp_protocol_handler/server.d.ts +61 -0
  181. package/dist/solidlsp/lsp_protocol_handler/server.js +68 -0
  182. package/dist/solidlsp/util/subprocess_util.d.ts +6 -0
  183. package/dist/solidlsp/util/subprocess_util.js +11 -0
  184. package/dist/solidlsp/util/zip.d.ts +25 -0
  185. package/dist/solidlsp/util/zip.js +188 -0
  186. package/package.json +65 -0
@@ -0,0 +1,694 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { createSerenaLogger } from '../../serena/util/logging.js';
7
+ import { Language } from '../ls_config.js';
8
+ import { ensureDefaultSubprocessOptions } from '../util/subprocess_util.js';
9
+ import { SolidLanguageServer, SolidLspSettings, registerLanguageServer, coerceLogLevel } from '../ls.js';
10
+ import { NodeLanguageServerHandler } from '../ls_handler.js';
11
+ import { RuntimeDependencyCollection } from './common.js';
12
+ import { SafeZipExtractor } from '../util/zip.js';
13
+ const NUGET_SERVICE_INDEX = 'https://pkgs.dev.azure.com/azure-public/vside/_packaging/vs-impl/nuget/v3/index.json';
14
+ const RUNTIME_DEPENDENCIES = [
15
+ {
16
+ id: 'CSharpLanguageServer',
17
+ packageName: 'Microsoft.CodeAnalysis.LanguageServer.win-x64',
18
+ packageVersion: '5.0.0-1.25329.6',
19
+ platformId: 'win-x64',
20
+ archiveType: 'nupkg',
21
+ binaryName: 'Microsoft.CodeAnalysis.LanguageServer.dll',
22
+ extractPath: 'content/LanguageServer/win-x64'
23
+ },
24
+ {
25
+ id: 'CSharpLanguageServer',
26
+ packageName: 'Microsoft.CodeAnalysis.LanguageServer.win-arm64',
27
+ packageVersion: '5.0.0-1.25329.6',
28
+ platformId: 'win-arm64',
29
+ archiveType: 'nupkg',
30
+ binaryName: 'Microsoft.CodeAnalysis.LanguageServer.dll',
31
+ extractPath: 'content/LanguageServer/win-arm64'
32
+ },
33
+ {
34
+ id: 'CSharpLanguageServer',
35
+ packageName: 'Microsoft.CodeAnalysis.LanguageServer.osx-x64',
36
+ packageVersion: '5.0.0-1.25329.6',
37
+ platformId: 'osx-x64',
38
+ archiveType: 'nupkg',
39
+ binaryName: 'Microsoft.CodeAnalysis.LanguageServer.dll',
40
+ extractPath: 'content/LanguageServer/osx-x64'
41
+ },
42
+ {
43
+ id: 'CSharpLanguageServer',
44
+ packageName: 'Microsoft.CodeAnalysis.LanguageServer.osx-arm64',
45
+ packageVersion: '5.0.0-1.25329.6',
46
+ platformId: 'osx-arm64',
47
+ archiveType: 'nupkg',
48
+ binaryName: 'Microsoft.CodeAnalysis.LanguageServer.dll',
49
+ extractPath: 'content/LanguageServer/osx-arm64'
50
+ },
51
+ {
52
+ id: 'CSharpLanguageServer',
53
+ packageName: 'Microsoft.CodeAnalysis.LanguageServer.linux-x64',
54
+ packageVersion: '5.0.0-1.25329.6',
55
+ platformId: 'linux-x64',
56
+ archiveType: 'nupkg',
57
+ binaryName: 'Microsoft.CodeAnalysis.LanguageServer.dll',
58
+ extractPath: 'content/LanguageServer/linux-x64'
59
+ },
60
+ {
61
+ id: 'CSharpLanguageServer',
62
+ packageName: 'Microsoft.CodeAnalysis.LanguageServer.linux-arm64',
63
+ packageVersion: '5.0.0-1.25329.6',
64
+ platformId: 'linux-arm64',
65
+ archiveType: 'nupkg',
66
+ binaryName: 'Microsoft.CodeAnalysis.LanguageServer.dll',
67
+ extractPath: 'content/LanguageServer/linux-arm64'
68
+ },
69
+ {
70
+ id: 'DotNetRuntime',
71
+ url: 'https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-win-x64.zip',
72
+ platformId: 'win-x64',
73
+ archiveType: 'zip',
74
+ binaryName: 'dotnet.exe'
75
+ },
76
+ {
77
+ id: 'DotNetRuntime',
78
+ url: 'https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-win-arm64.zip',
79
+ platformId: 'win-arm64',
80
+ archiveType: 'zip',
81
+ binaryName: 'dotnet.exe'
82
+ },
83
+ {
84
+ id: 'DotNetRuntime',
85
+ url: 'https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-linux-x64.tar.gz',
86
+ platformId: 'linux-x64',
87
+ archiveType: 'tar.gz',
88
+ binaryName: 'dotnet'
89
+ },
90
+ {
91
+ id: 'DotNetRuntime',
92
+ url: 'https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-linux-arm64.tar.gz',
93
+ platformId: 'linux-arm64',
94
+ archiveType: 'tar.gz',
95
+ binaryName: 'dotnet'
96
+ },
97
+ {
98
+ id: 'DotNetRuntime',
99
+ url: 'https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-osx-x64.tar.gz',
100
+ platformId: 'osx-x64',
101
+ archiveType: 'tar.gz',
102
+ binaryName: 'dotnet'
103
+ },
104
+ {
105
+ id: 'DotNetRuntime',
106
+ url: 'https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-osx-arm64.tar.gz',
107
+ platformId: 'osx-arm64',
108
+ archiveType: 'tar.gz',
109
+ binaryName: 'dotnet'
110
+ }
111
+ ];
112
+ const CSHARP_IGNORED_DIRECTORIES = new Set(['bin', 'obj', 'packages', '.vs']);
113
+ export class CSharpLanguageServer extends SolidLanguageServer {
114
+ handler;
115
+ initialized = false;
116
+ constructor(config, loggerLike, repositoryRootPath, options = {}) {
117
+ const augmentedConfig = {
118
+ ...config,
119
+ ignoredPaths: mergeIgnoredDirectories(config.ignoredPaths)
120
+ };
121
+ const solidSettings = new SolidLspSettings(options?.solidlspSettings);
122
+ const runtimeDir = resolveRuntimeDirectory(solidSettings);
123
+ const languageSettings = normalizeLanguageSettings(solidSettings.lsSpecificSettings?.[Language.CSHARP]);
124
+ const { logger: installLogger } = createSerenaLogger({
125
+ name: 'solidlsp.language_servers.csharp',
126
+ emitToConsole: false,
127
+ level: loggerLike?.level === undefined ? undefined : coerceLogLevel(loggerLike.level)
128
+ });
129
+ const { dotnetPath, languageServerPath } = ensureRuntimeAssets({
130
+ runtimeDir,
131
+ logger: installLogger,
132
+ overrides: languageSettings.runtime_dependencies ?? [],
133
+ dotnetRuntimeUrlOverride: languageSettings.dotnet_runtime_url
134
+ });
135
+ const logDir = path.join(runtimeDir, 'logs');
136
+ fs.mkdirSync(logDir, { recursive: true });
137
+ const processInfo = {
138
+ cmd: buildCommand(dotnetPath, languageServerPath, logDir),
139
+ cwd: repositoryRootPath
140
+ };
141
+ const handler = new NodeLanguageServerHandler(processInfo, {
142
+ requestTimeoutSeconds: options?.timeout ?? null
143
+ });
144
+ super(augmentedConfig, loggerLike, repositoryRootPath, {
145
+ ...options,
146
+ handler,
147
+ solidlspSettings: options?.solidlspSettings
148
+ });
149
+ this.handler = handler;
150
+ this.registerHandlers();
151
+ }
152
+ start() {
153
+ const shouldInitialize = !this.initialized;
154
+ super.start();
155
+ if (shouldInitialize) {
156
+ this.initializeLanguageServer();
157
+ this.initialized = true;
158
+ }
159
+ return this;
160
+ }
161
+ stop(shutdownTimeout = 2.0) {
162
+ super.stop(shutdownTimeout);
163
+ this.initialized = false;
164
+ }
165
+ registerHandlers() {
166
+ const noop = () => undefined;
167
+ this.handler.onNotification('window/logMessage', (payload) => {
168
+ const message = extractLogMessage(payload);
169
+ if (message) {
170
+ this.logger.info(`C# LS: ${message}`);
171
+ }
172
+ });
173
+ this.handler.onNotification('$/progress', noop);
174
+ this.handler.onNotification('textDocument/publishDiagnostics', noop);
175
+ this.handler.onRequest('workspace/configuration', (params) => {
176
+ const items = params?.items;
177
+ return Array.isArray(items) ? items.map(() => ({})) : [];
178
+ });
179
+ this.handler.onRequest('window/workDoneProgress/create', noop);
180
+ this.handler.onRequest('client/registerCapability', () => []);
181
+ this.handler.onRequest('workspace/_roslyn_projectNeedsRestore', noop);
182
+ }
183
+ initializeLanguageServer() {
184
+ const params = this.buildInitializeParams();
185
+ const response = this.handler.sendRequest('initialize', params);
186
+ if (!response || typeof response !== 'object') {
187
+ throw new Error('C# language server returned an invalid initialize response.');
188
+ }
189
+ this.applyDiagnosticCapabilities(response);
190
+ this.verifyCapabilities(response.capabilities ?? null);
191
+ this.handler.notify.initialized({});
192
+ this.openSolutionAndProjects();
193
+ }
194
+ buildInitializeParams() {
195
+ const rootUri = pathToFileURL(this.repositoryRootPath).href;
196
+ return {
197
+ workspaceFolders: [
198
+ {
199
+ uri: rootUri,
200
+ name: path.basename(this.repositoryRootPath)
201
+ }
202
+ ],
203
+ processId: process.pid,
204
+ rootPath: this.repositoryRootPath,
205
+ rootUri,
206
+ capabilities: buildClientCapabilities()
207
+ };
208
+ }
209
+ applyDiagnosticCapabilities(response) {
210
+ if (!isRecord(response.capabilities)) {
211
+ return;
212
+ }
213
+ const capabilities = response.capabilities;
214
+ const diagnosticProvider = capabilities.diagnosticProvider;
215
+ if (diagnosticProvider && typeof diagnosticProvider === 'object') {
216
+ Object.assign(diagnosticProvider, {
217
+ interFileDependencies: true,
218
+ workDoneProgress: true,
219
+ workspaceDiagnostics: true
220
+ });
221
+ }
222
+ else {
223
+ capabilities.diagnosticProvider = {
224
+ interFileDependencies: true,
225
+ workDoneProgress: true,
226
+ workspaceDiagnostics: true
227
+ };
228
+ }
229
+ }
230
+ verifyCapabilities(capabilities) {
231
+ if (!capabilities) {
232
+ throw new Error('C# language server initialization response is missing capabilities.');
233
+ }
234
+ const required = ['textDocumentSync', 'definitionProvider', 'referencesProvider', 'documentSymbolProvider'];
235
+ const missing = required.filter((key) => !(key in capabilities));
236
+ if (missing.length > 0) {
237
+ throw new Error(`C# language server is missing required capabilities: ${missing.join(', ')}. Ensure Microsoft.CodeAnalysis.LanguageServer is installed.`);
238
+ }
239
+ }
240
+ openSolutionAndProjects() {
241
+ const solutionFile = findSolutionOrProjectFile(this.repositoryRootPath, '.sln');
242
+ if (solutionFile) {
243
+ const solutionUri = pathToFileURL(solutionFile).href;
244
+ this.handler.sendNotification('solution/open', { solution: solutionUri });
245
+ this.logger.info(`Opened solution file: ${solutionFile}`);
246
+ }
247
+ const projectFiles = findAllProjectFiles(this.repositoryRootPath, '.csproj');
248
+ if (projectFiles.length > 0) {
249
+ const projectUris = projectFiles.map((project) => pathToFileURL(project).href);
250
+ this.handler.sendNotification('project/open', { projects: projectUris });
251
+ this.logger.debug(`Opened project files: ${projectFiles.join(', ')}`);
252
+ }
253
+ }
254
+ }
255
+ registerLanguageServer(Language.CSHARP, CSharpLanguageServer);
256
+ function ensureRuntimeAssets(options) {
257
+ const { runtimeDir, logger, overrides, dotnetRuntimeUrlOverride } = options;
258
+ const dotnetDir = path.join(runtimeDir, 'dotnet-runtime-9.0');
259
+ const languageServerDir = path.join(runtimeDir, 'language-server');
260
+ const dotnetDependency = resolveDependency('DotNetRuntime', overrides);
261
+ const languageDependency = resolveDependency('CSharpLanguageServer', overrides);
262
+ const dotnetPath = ensureDotnetRuntime({
263
+ logger,
264
+ targetDir: dotnetDir,
265
+ dependency: applyUrlOverride(dotnetDependency, dotnetRuntimeUrlOverride)
266
+ });
267
+ const languageServerPath = ensureLanguageServer({
268
+ logger,
269
+ targetDir: languageServerDir,
270
+ dependency: languageDependency
271
+ });
272
+ return { dotnetPath, languageServerPath };
273
+ }
274
+ function ensureDotnetRuntime(options) {
275
+ const { logger, targetDir, dependency } = options;
276
+ const systemDotnet = findDotnetRuntimeFromSystem(logger);
277
+ if (systemDotnet) {
278
+ return systemDotnet;
279
+ }
280
+ const binaryPath = path.join(targetDir, dependency.binaryName ?? inferDotnetBinaryName());
281
+ if (fs.existsSync(binaryPath)) {
282
+ return binaryPath;
283
+ }
284
+ if (process.env.SERENA_SKIP_RUNTIME_INSTALL === '1') {
285
+ throw new Error(`dotnet runtime was not found at ${binaryPath}. Set SERENA_SKIP_RUNTIME_INSTALL=0 to allow downloads or place dotnet manually.`);
286
+ }
287
+ fs.mkdirSync(targetDir, { recursive: true });
288
+ logger.info('Downloading .NET runtime for C# language server.');
289
+ const collection = new RuntimeDependencyCollection([dependency]);
290
+ collection.install(logger, targetDir);
291
+ if (!fs.existsSync(binaryPath)) {
292
+ throw new Error(`dotnet binary not found after installation (expected at ${binaryPath}).`);
293
+ }
294
+ if (process.platform !== 'win32') {
295
+ try {
296
+ fs.chmodSync(binaryPath, 0o755);
297
+ }
298
+ catch {
299
+ // best effort
300
+ }
301
+ }
302
+ return binaryPath;
303
+ }
304
+ function ensureLanguageServer(options) {
305
+ const { logger, targetDir, dependency } = options;
306
+ const version = dependency.packageVersion ?? 'unknown';
307
+ const packageName = dependency.packageName ?? 'Microsoft.CodeAnalysis.LanguageServer';
308
+ const installationDir = path.join(targetDir, `${packageName}.${version}`);
309
+ const dllPath = path.join(installationDir, dependency.binaryName ?? 'Microsoft.CodeAnalysis.LanguageServer.dll');
310
+ if (fs.existsSync(dllPath)) {
311
+ return dllPath;
312
+ }
313
+ if (process.env.SERENA_SKIP_RUNTIME_INSTALL === '1') {
314
+ throw new Error(`C# language server DLL not found at ${dllPath}. Allow downloads or place Microsoft.CodeAnalysis.LanguageServer manually.`);
315
+ }
316
+ fs.mkdirSync(targetDir, { recursive: true });
317
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'serena-csharp-ls-'));
318
+ try {
319
+ const extractDir = path.join(tempDir, 'extracted');
320
+ fs.mkdirSync(extractDir, { recursive: true });
321
+ const packagePath = downloadNugetPackage({
322
+ logger,
323
+ dependency,
324
+ extractDir
325
+ });
326
+ copyDependencyPayload({
327
+ dependency,
328
+ packageRoot: packagePath,
329
+ targetDir: installationDir
330
+ });
331
+ }
332
+ finally {
333
+ safeRemove(tempDir);
334
+ }
335
+ if (!fs.existsSync(dllPath)) {
336
+ throw new Error(`Microsoft.CodeAnalysis.LanguageServer.dll not found after extraction (expected at ${dllPath}).`);
337
+ }
338
+ if (process.platform !== 'win32') {
339
+ try {
340
+ fs.chmodSync(dllPath, 0o755);
341
+ }
342
+ catch {
343
+ // ignore chmod errors
344
+ }
345
+ }
346
+ return dllPath;
347
+ }
348
+ function downloadNugetPackage(options) {
349
+ const { logger, dependency, extractDir } = options;
350
+ const packageName = dependency.packageName;
351
+ const packageVersion = dependency.packageVersion;
352
+ if (!packageName || !packageVersion) {
353
+ throw new Error('C# language server dependency must specify packageName and packageVersion.');
354
+ }
355
+ const serviceIndex = downloadServiceIndex(logger);
356
+ const baseAddress = findPackageBaseAddress(serviceIndex);
357
+ const packageUrl = buildPackageUrl(baseAddress, packageName, packageVersion);
358
+ const archivePath = path.join(extractDir, `${packageName}.${packageVersion}.nupkg`);
359
+ downloadWithCurl(packageUrl, archivePath, logger);
360
+ const packageRoot = path.join(extractDir, `${packageName}.${packageVersion}`);
361
+ fs.mkdirSync(packageRoot, { recursive: true });
362
+ extractZipArchive(archivePath, packageRoot);
363
+ return packageRoot;
364
+ }
365
+ function downloadServiceIndex(logger) {
366
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'serena-nuget-index-'));
367
+ const indexPath = path.join(tempDir, 'index.json');
368
+ try {
369
+ downloadWithCurl(NUGET_SERVICE_INDEX, indexPath, logger);
370
+ const data = fs.readFileSync(indexPath, 'utf-8');
371
+ return JSON.parse(data);
372
+ }
373
+ finally {
374
+ safeRemove(tempDir);
375
+ }
376
+ }
377
+ function findPackageBaseAddress(serviceIndex) {
378
+ if (!serviceIndex || typeof serviceIndex !== 'object') {
379
+ throw new Error('Invalid NuGet service index payload.');
380
+ }
381
+ const resources = serviceIndex.resources;
382
+ if (!Array.isArray(resources)) {
383
+ throw new Error('NuGet service index is missing resources array.');
384
+ }
385
+ for (const entry of resources) {
386
+ const baseAddress = entry?.['@id'];
387
+ if (entry?.['@type'] === 'PackageBaseAddress/3.0.0' && typeof baseAddress === 'string') {
388
+ return baseAddress;
389
+ }
390
+ }
391
+ throw new Error('Failed to locate NuGet PackageBaseAddress in service index.');
392
+ }
393
+ function buildPackageUrl(baseAddress, packageName, packageVersion) {
394
+ const lowerName = packageName.toLowerCase();
395
+ const lowerVersion = packageVersion.toLowerCase();
396
+ return `${baseAddress.replace(/\/?$/, '/')}${lowerName}/${lowerVersion}/${lowerName}.${lowerVersion}.nupkg`;
397
+ }
398
+ function copyDependencyPayload(options) {
399
+ const { dependency, packageRoot, targetDir } = options;
400
+ const extractPath = dependency.extractPath ?? 'lib/net9.0';
401
+ const primaryPath = path.join(packageRoot, extractPath);
402
+ const fallbackLocations = [
403
+ path.join(packageRoot, 'tools', 'net9.0', 'any'),
404
+ path.join(packageRoot, 'lib', 'net9.0'),
405
+ path.join(packageRoot, 'contentFiles', 'any', 'net9.0')
406
+ ];
407
+ const candidates = [primaryPath, ...fallbackLocations];
408
+ const existing = candidates.find((candidate) => fs.existsSync(candidate));
409
+ if (!existing) {
410
+ throw new Error(`Could not locate language server payload inside ${packageRoot}.`);
411
+ }
412
+ fs.mkdirSync(targetDir, { recursive: true });
413
+ copyDirectory(existing, targetDir);
414
+ }
415
+ function mergeIgnoredDirectories(existing) {
416
+ const set = new Set(existing ?? []);
417
+ for (const entry of CSHARP_IGNORED_DIRECTORIES) {
418
+ set.add(entry);
419
+ set.add(`**/${entry}`);
420
+ }
421
+ return Array.from(set);
422
+ }
423
+ function resolveRuntimeDirectory(settings) {
424
+ const dir = path.join(settings.languageServersStaticDir, 'csharp');
425
+ fs.mkdirSync(dir, { recursive: true });
426
+ return dir;
427
+ }
428
+ function normalizeLanguageSettings(raw) {
429
+ if (!raw || typeof raw !== 'object') {
430
+ return {};
431
+ }
432
+ const settings = raw;
433
+ const runtimeOverrides = [];
434
+ const maybeOverrides = settings.runtime_dependencies;
435
+ if (Array.isArray(maybeOverrides)) {
436
+ for (const entry of maybeOverrides) {
437
+ if (entry && typeof entry === 'object') {
438
+ runtimeOverrides.push(entry);
439
+ }
440
+ }
441
+ }
442
+ return {
443
+ runtime_dependencies: runtimeOverrides,
444
+ dotnet_runtime_url: typeof settings.dotnet_runtime_url === 'string' ? settings.dotnet_runtime_url : undefined
445
+ };
446
+ }
447
+ function resolveDependency(id, overrides) {
448
+ const platform = currentPlatformId();
449
+ const candidates = RUNTIME_DEPENDENCIES.filter((entry) => entry.id === id && matchesPlatform(entry.platformId, platform));
450
+ if (candidates.length === 0) {
451
+ throw new Error(`No runtime dependency found for ${id} on platform ${platform ?? 'unknown'}.`);
452
+ }
453
+ const base = { ...candidates[0] };
454
+ const override = overrides.find((entry) => entry.id === id && matchesPlatform(entry.platformId, platform));
455
+ return override ? { ...base, ...override } : base;
456
+ }
457
+ function matchesPlatform(candidate, platform) {
458
+ if (!candidate || candidate === 'any' || candidate === 'platform-agnostic') {
459
+ return true;
460
+ }
461
+ if (!platform) {
462
+ return false;
463
+ }
464
+ return candidate.toLowerCase() === platform.toLowerCase();
465
+ }
466
+ function currentPlatformId() {
467
+ if (process.platform === 'win32') {
468
+ return process.arch === 'arm64' ? 'win-arm64' : 'win-x64';
469
+ }
470
+ if (process.platform === 'darwin') {
471
+ return process.arch === 'arm64' ? 'osx-arm64' : 'osx-x64';
472
+ }
473
+ if (process.platform === 'linux') {
474
+ if (process.arch === 'arm64') {
475
+ return 'linux-arm64';
476
+ }
477
+ return 'linux-x64';
478
+ }
479
+ return null;
480
+ }
481
+ function findDotnetRuntimeFromSystem(logger) {
482
+ const dotnetPath = whichBinary('dotnet');
483
+ if (!dotnetPath) {
484
+ return null;
485
+ }
486
+ const result = spawnSync(dotnetPath, ['--list-runtimes'], ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
487
+ if (result.status === 0 && typeof result.stdout === 'string' && result.stdout.includes('Microsoft.NETCore.App 9.')) {
488
+ logger.info('Found system .NET 9 runtime.');
489
+ return dotnetPath;
490
+ }
491
+ return null;
492
+ }
493
+ function whichBinary(command) {
494
+ const locator = process.platform === 'win32' ? 'where' : 'which';
495
+ const result = spawnSync(locator, [command], ensureDefaultSubprocessOptions({ encoding: 'utf-8' }));
496
+ if (result.status === 0 && typeof result.stdout === 'string') {
497
+ const [first] = result.stdout.split(/\r?\n/);
498
+ if (first && first.trim().length > 0) {
499
+ return first.trim();
500
+ }
501
+ }
502
+ return null;
503
+ }
504
+ function inferDotnetBinaryName() {
505
+ return process.platform === 'win32' ? 'dotnet.exe' : 'dotnet';
506
+ }
507
+ function buildCommand(dotnetPath, languageServerPath, logDir) {
508
+ return [
509
+ dotnetPath,
510
+ languageServerPath,
511
+ '--logLevel=Information',
512
+ `--extensionLogDirectory=${quoteForArgument(logDir)}`,
513
+ '--stdio'
514
+ ];
515
+ }
516
+ function extractLogMessage(payload) {
517
+ if (!payload || typeof payload !== 'object') {
518
+ return null;
519
+ }
520
+ const value = payload.message;
521
+ return typeof value === 'string' ? value : null;
522
+ }
523
+ function downloadWithCurl(url, destination, logger) {
524
+ logger.info(`Downloading ${url}`);
525
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
526
+ const args = ['-L', url, '-o', destination];
527
+ const result = spawnSync('curl', args, ensureDefaultSubprocessOptions({ stdio: 'inherit' }));
528
+ if (result.status === 0) {
529
+ return;
530
+ }
531
+ if (process.platform !== 'win32') {
532
+ const wget = spawnSync('wget', ['-O', destination, url], ensureDefaultSubprocessOptions({ stdio: 'inherit' }));
533
+ if (wget.status === 0) {
534
+ return;
535
+ }
536
+ }
537
+ else {
538
+ const ps = spawnSync('powershell', [
539
+ '-NoLogo',
540
+ '-NoProfile',
541
+ '-ExecutionPolicy',
542
+ 'Bypass',
543
+ '-Command',
544
+ `Invoke-WebRequest -Uri ${JSON.stringify(url)} -OutFile ${JSON.stringify(destination)} -UseBasicParsing`
545
+ ], ensureDefaultSubprocessOptions({ stdio: 'inherit' }));
546
+ if (ps.status === 0) {
547
+ return;
548
+ }
549
+ }
550
+ throw new Error(`Failed to download ${url}. Ensure curl, wget, or PowerShell is available.`);
551
+ }
552
+ function extractZipArchive(archivePath, targetDir) {
553
+ const extractor = new SafeZipExtractor(archivePath, targetDir, { verbose: false });
554
+ extractor.extractAll();
555
+ }
556
+ function copyDirectory(source, destination) {
557
+ fs.mkdirSync(destination, { recursive: true });
558
+ const entries = fs.readdirSync(source, { withFileTypes: true });
559
+ for (const entry of entries) {
560
+ const sourcePath = path.join(source, entry.name);
561
+ const destinationPath = path.join(destination, entry.name);
562
+ if (entry.isDirectory()) {
563
+ copyDirectory(sourcePath, destinationPath);
564
+ }
565
+ else if (entry.isFile()) {
566
+ fs.copyFileSync(sourcePath, destinationPath);
567
+ if (process.platform !== 'win32') {
568
+ try {
569
+ fs.chmodSync(destinationPath, 0o755);
570
+ }
571
+ catch {
572
+ // ignore chmod failures
573
+ }
574
+ }
575
+ }
576
+ }
577
+ }
578
+ function safeRemove(targetPath) {
579
+ try {
580
+ fs.rmSync(targetPath, { recursive: true, force: true });
581
+ }
582
+ catch {
583
+ // best effort cleanup
584
+ }
585
+ }
586
+ function buildClientCapabilities() {
587
+ const valueSet = Array.from({ length: 27 }, (_, index) => index + 1);
588
+ return {
589
+ window: {
590
+ workDoneProgress: true,
591
+ showMessage: { messageActionItem: { additionalPropertiesSupport: true } },
592
+ showDocument: { support: true }
593
+ },
594
+ workspace: {
595
+ applyEdit: true,
596
+ workspaceEdit: { documentChanges: true },
597
+ didChangeConfiguration: { dynamicRegistration: true },
598
+ didChangeWatchedFiles: { dynamicRegistration: true },
599
+ symbol: {
600
+ dynamicRegistration: true,
601
+ symbolKind: { valueSet }
602
+ },
603
+ executeCommand: { dynamicRegistration: true }
604
+ },
605
+ textDocument: {
606
+ synchronization: {
607
+ dynamicRegistration: true,
608
+ willSave: true,
609
+ willSaveWaitUntil: true,
610
+ didSave: true
611
+ },
612
+ hover: {
613
+ dynamicRegistration: true,
614
+ contentFormat: ['markdown', 'plaintext']
615
+ },
616
+ signatureHelp: {
617
+ dynamicRegistration: true,
618
+ signatureInformation: {
619
+ documentationFormat: ['markdown', 'plaintext'],
620
+ parameterInformation: { labelOffsetSupport: true }
621
+ }
622
+ },
623
+ definition: { dynamicRegistration: true },
624
+ references: { dynamicRegistration: true },
625
+ documentSymbol: {
626
+ dynamicRegistration: true,
627
+ hierarchicalDocumentSymbolSupport: true,
628
+ symbolKind: { valueSet }
629
+ }
630
+ }
631
+ };
632
+ }
633
+ function findSolutionOrProjectFile(rootDir, extension) {
634
+ for (const candidate of breadthFirstFileScan(rootDir)) {
635
+ if (candidate.toLowerCase().endsWith(extension.toLowerCase())) {
636
+ return candidate;
637
+ }
638
+ }
639
+ return null;
640
+ }
641
+ function findAllProjectFiles(rootDir, extension) {
642
+ const results = [];
643
+ for (const candidate of breadthFirstFileScan(rootDir)) {
644
+ if (candidate.toLowerCase().endsWith(extension.toLowerCase())) {
645
+ results.push(candidate);
646
+ }
647
+ }
648
+ return results;
649
+ }
650
+ function* breadthFirstFileScan(rootDir) {
651
+ const queue = [rootDir];
652
+ const visited = new Set();
653
+ while (queue.length > 0) {
654
+ const current = queue.shift();
655
+ if (!current || visited.has(current)) {
656
+ continue;
657
+ }
658
+ visited.add(current);
659
+ let entries = [];
660
+ try {
661
+ entries = fs.readdirSync(current, { withFileTypes: true });
662
+ }
663
+ catch {
664
+ continue;
665
+ }
666
+ for (const entry of entries) {
667
+ if (entry.name.startsWith('.')) {
668
+ continue;
669
+ }
670
+ const candidatePath = path.join(current, entry.name);
671
+ if (entry.isDirectory()) {
672
+ queue.push(candidatePath);
673
+ }
674
+ else if (entry.isFile()) {
675
+ yield candidatePath;
676
+ }
677
+ }
678
+ }
679
+ }
680
+ function applyUrlOverride(dep, overrideUrl) {
681
+ if (!overrideUrl) {
682
+ return dep;
683
+ }
684
+ return { ...dep, url: overrideUrl };
685
+ }
686
+ function quoteForArgument(value) {
687
+ if (/\s/.test(value)) {
688
+ return `"${value}"`;
689
+ }
690
+ return value;
691
+ }
692
+ function isRecord(value) {
693
+ return typeof value === 'object' && value !== null;
694
+ }