@kernel.chat/kbot 3.51.0 → 3.52.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.
Files changed (81) hide show
  1. package/README.md +43 -9
  2. package/dist/agent-protocol.test.d.ts +2 -0
  3. package/dist/agent-protocol.test.d.ts.map +1 -0
  4. package/dist/agent-protocol.test.js +730 -0
  5. package/dist/agent-protocol.test.js.map +1 -0
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +34 -10
  8. package/dist/agent.js.map +1 -1
  9. package/dist/auth.js +3 -3
  10. package/dist/auth.js.map +1 -1
  11. package/dist/bench.d.ts +64 -0
  12. package/dist/bench.d.ts.map +1 -0
  13. package/dist/bench.js +973 -0
  14. package/dist/bench.js.map +1 -0
  15. package/dist/cli.js +144 -29
  16. package/dist/cli.js.map +1 -1
  17. package/dist/cloud-agent.d.ts +77 -0
  18. package/dist/cloud-agent.d.ts.map +1 -0
  19. package/dist/cloud-agent.js +743 -0
  20. package/dist/cloud-agent.js.map +1 -0
  21. package/dist/context.test.d.ts +2 -0
  22. package/dist/context.test.d.ts.map +1 -0
  23. package/dist/context.test.js +561 -0
  24. package/dist/context.test.js.map +1 -0
  25. package/dist/evolution.d.ts.map +1 -1
  26. package/dist/evolution.js +4 -1
  27. package/dist/evolution.js.map +1 -1
  28. package/dist/github-release.d.ts +61 -0
  29. package/dist/github-release.d.ts.map +1 -0
  30. package/dist/github-release.js +451 -0
  31. package/dist/github-release.js.map +1 -0
  32. package/dist/graph-memory.test.d.ts +2 -0
  33. package/dist/graph-memory.test.d.ts.map +1 -0
  34. package/dist/graph-memory.test.js +946 -0
  35. package/dist/graph-memory.test.js.map +1 -0
  36. package/dist/init-science.d.ts +43 -0
  37. package/dist/init-science.d.ts.map +1 -0
  38. package/dist/init-science.js +477 -0
  39. package/dist/init-science.js.map +1 -0
  40. package/dist/lab.d.ts +45 -0
  41. package/dist/lab.d.ts.map +1 -0
  42. package/dist/lab.js +1020 -0
  43. package/dist/lab.js.map +1 -0
  44. package/dist/lsp-deep.d.ts +101 -0
  45. package/dist/lsp-deep.d.ts.map +1 -0
  46. package/dist/lsp-deep.js +689 -0
  47. package/dist/lsp-deep.js.map +1 -0
  48. package/dist/memory.test.d.ts +2 -0
  49. package/dist/memory.test.d.ts.map +1 -0
  50. package/dist/memory.test.js +369 -0
  51. package/dist/memory.test.js.map +1 -0
  52. package/dist/multi-session.d.ts +164 -0
  53. package/dist/multi-session.d.ts.map +1 -0
  54. package/dist/multi-session.js +885 -0
  55. package/dist/multi-session.js.map +1 -0
  56. package/dist/self-eval.d.ts.map +1 -1
  57. package/dist/self-eval.js +5 -2
  58. package/dist/self-eval.js.map +1 -1
  59. package/dist/streaming.d.ts.map +1 -1
  60. package/dist/streaming.js +0 -1
  61. package/dist/streaming.js.map +1 -1
  62. package/dist/teach.d.ts +136 -0
  63. package/dist/teach.d.ts.map +1 -0
  64. package/dist/teach.js +915 -0
  65. package/dist/teach.js.map +1 -0
  66. package/dist/telemetry.d.ts +1 -1
  67. package/dist/telemetry.d.ts.map +1 -1
  68. package/dist/telemetry.js.map +1 -1
  69. package/dist/tools/browser-agent.js +2 -2
  70. package/dist/tools/browser-agent.js.map +1 -1
  71. package/dist/tools/forge.d.ts.map +1 -1
  72. package/dist/tools/forge.js +15 -26
  73. package/dist/tools/forge.js.map +1 -1
  74. package/dist/tools/git.d.ts.map +1 -1
  75. package/dist/tools/git.js +10 -7
  76. package/dist/tools/git.js.map +1 -1
  77. package/dist/voice-realtime.d.ts +54 -0
  78. package/dist/voice-realtime.d.ts.map +1 -0
  79. package/dist/voice-realtime.js +805 -0
  80. package/dist/voice-realtime.js.map +1 -0
  81. package/package.json +10 -3
@@ -0,0 +1,689 @@
1
+ // kbot Deep LSP Integration
2
+ //
3
+ // Wires language servers deeper into the agent loop:
4
+ // 1. Auto-attach to the right LSP server for the project
5
+ // 2. Proactive diagnostics monitoring injected into agent context
6
+ // 3. Type-aware file context enrichment (hover/type info)
7
+ // 4. Smart reference checking before modifying symbols
8
+ // 5. Auto-fix suggestions for common diagnostic codes
9
+ //
10
+ // Unlike lsp-bridge.ts (one-shot diagnostics), this module maintains
11
+ // a persistent LSP session for continuous intelligence.
12
+ import { spawn } from 'node:child_process';
13
+ import { existsSync, readdirSync } from 'node:fs';
14
+ import { join, extname, resolve, relative } from 'node:path';
15
+ import { execSync } from 'node:child_process';
16
+ const LSP_SERVERS = {
17
+ typescript: {
18
+ command: ['typescript-language-server', '--stdio'],
19
+ languageIds: ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'],
20
+ extensions: ['.ts', '.tsx', '.js', '.jsx'],
21
+ detectBinary: 'typescript-language-server',
22
+ },
23
+ python: {
24
+ command: ['pyright-langserver', '--stdio'],
25
+ languageIds: ['python'],
26
+ extensions: ['.py'],
27
+ detectBinary: 'pyright-langserver',
28
+ },
29
+ 'python-pylsp': {
30
+ command: ['pylsp'],
31
+ languageIds: ['python'],
32
+ extensions: ['.py'],
33
+ detectBinary: 'pylsp',
34
+ },
35
+ rust: {
36
+ command: ['rust-analyzer'],
37
+ languageIds: ['rust'],
38
+ extensions: ['.rs'],
39
+ detectBinary: 'rust-analyzer',
40
+ },
41
+ go: {
42
+ command: ['gopls', 'serve'],
43
+ languageIds: ['go', 'gomod'],
44
+ extensions: ['.go'],
45
+ detectBinary: 'gopls',
46
+ },
47
+ clangd: {
48
+ command: ['clangd'],
49
+ languageIds: ['c', 'cpp', 'objc', 'objcpp'],
50
+ extensions: ['.c', '.cpp', '.cc', '.cxx', '.h', '.hpp'],
51
+ detectBinary: 'clangd',
52
+ },
53
+ java: {
54
+ command: ['jdtls'],
55
+ languageIds: ['java'],
56
+ extensions: ['.java'],
57
+ detectBinary: 'jdtls',
58
+ },
59
+ lua: {
60
+ command: ['lua-language-server'],
61
+ languageIds: ['lua'],
62
+ extensions: ['.lua'],
63
+ detectBinary: 'lua-language-server',
64
+ },
65
+ ruby: {
66
+ command: ['solargraph', 'stdio'],
67
+ languageIds: ['ruby'],
68
+ extensions: ['.rb'],
69
+ detectBinary: 'solargraph',
70
+ },
71
+ zig: {
72
+ command: ['zls'],
73
+ languageIds: ['zig'],
74
+ extensions: ['.zig'],
75
+ detectBinary: 'zls',
76
+ },
77
+ };
78
+ // ── Internal State ──
79
+ let activeServer = null;
80
+ let activeConfig = null;
81
+ let activeProjectDir = null;
82
+ let messageId = 0;
83
+ let buffer = '';
84
+ let initialized = false;
85
+ let fileWatcher = null;
86
+ const context = {
87
+ diagnostics: [],
88
+ symbols: [],
89
+ references: new Map(),
90
+ typeInfo: new Map(),
91
+ serverCapabilities: {},
92
+ };
93
+ // Pending request callbacks
94
+ const pendingRequests = new Map();
95
+ function encodeMessage(msg) {
96
+ const body = JSON.stringify(msg);
97
+ return `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`;
98
+ }
99
+ function parseMessages(data) {
100
+ const messages = [];
101
+ let remaining = data;
102
+ while (remaining.length > 0) {
103
+ const headerEnd = remaining.indexOf('\r\n\r\n');
104
+ if (headerEnd === -1)
105
+ break;
106
+ const header = remaining.slice(0, headerEnd);
107
+ const lengthMatch = header.match(/Content-Length:\s*(\d+)/i);
108
+ if (!lengthMatch)
109
+ break;
110
+ const contentLength = parseInt(lengthMatch[1], 10);
111
+ const bodyStart = headerEnd + 4;
112
+ const body = remaining.slice(bodyStart, bodyStart + contentLength);
113
+ if (body.length < contentLength)
114
+ break;
115
+ try {
116
+ messages.push(JSON.parse(body));
117
+ }
118
+ catch {
119
+ if (process.env.KBOT_DEBUG) {
120
+ process.stderr.write('[lsp-deep] malformed JSON-RPC message\n');
121
+ }
122
+ }
123
+ remaining = remaining.slice(bodyStart + contentLength);
124
+ }
125
+ return { messages, remaining };
126
+ }
127
+ function mapSeverity(severity) {
128
+ switch (severity) {
129
+ case 1: return 'error';
130
+ case 2: return 'warning';
131
+ case 3: return 'info';
132
+ case 4: return 'hint';
133
+ default: return 'warning';
134
+ }
135
+ }
136
+ const SYMBOL_KIND_MAP = {
137
+ 1: 'file', 2: 'module', 3: 'namespace', 4: 'package', 5: 'class',
138
+ 6: 'method', 7: 'property', 8: 'field', 9: 'constructor', 10: 'enum',
139
+ 11: 'interface', 12: 'function', 13: 'variable', 14: 'constant', 15: 'string',
140
+ 16: 'number', 17: 'boolean', 18: 'array', 19: 'object', 20: 'key',
141
+ 21: 'null', 22: 'enum_member', 23: 'struct', 24: 'event', 25: 'operator',
142
+ 26: 'type_parameter',
143
+ };
144
+ function getLanguageId(filePath) {
145
+ const ext = extname(filePath).toLowerCase();
146
+ const map = {
147
+ '.ts': 'typescript', '.tsx': 'typescriptreact',
148
+ '.js': 'javascript', '.jsx': 'javascriptreact',
149
+ '.py': 'python', '.rs': 'rust', '.go': 'go',
150
+ '.c': 'c', '.cpp': 'cpp', '.cc': 'cpp', '.h': 'c', '.hpp': 'cpp',
151
+ '.java': 'java', '.rb': 'ruby', '.lua': 'lua', '.zig': 'zig',
152
+ };
153
+ return map[ext] || 'plaintext';
154
+ }
155
+ // ── LSP Communication ──
156
+ function sendRequest(method, params, timeoutMs = 10000) {
157
+ return new Promise((resolve, reject) => {
158
+ if (!activeServer?.stdin) {
159
+ reject(new Error('LSP server not running'));
160
+ return;
161
+ }
162
+ const id = ++messageId;
163
+ const timer = setTimeout(() => {
164
+ pendingRequests.delete(id);
165
+ reject(new Error(`LSP request ${method} timed out after ${timeoutMs}ms`));
166
+ }, timeoutMs);
167
+ pendingRequests.set(id, { resolve, reject, timer });
168
+ activeServer.stdin.write(encodeMessage({
169
+ jsonrpc: '2.0',
170
+ id,
171
+ method,
172
+ params,
173
+ }));
174
+ });
175
+ }
176
+ function sendNotification(method, params) {
177
+ if (!activeServer?.stdin)
178
+ return;
179
+ activeServer.stdin.write(encodeMessage({
180
+ jsonrpc: '2.0',
181
+ method,
182
+ params,
183
+ }));
184
+ }
185
+ function handleMessage(msg) {
186
+ // Response to a request
187
+ if (msg.id !== undefined && pendingRequests.has(msg.id)) {
188
+ const pending = pendingRequests.get(msg.id);
189
+ pendingRequests.delete(msg.id);
190
+ clearTimeout(pending.timer);
191
+ if (msg.error) {
192
+ pending.reject(new Error(msg.error.message));
193
+ }
194
+ else {
195
+ pending.resolve(msg.result);
196
+ }
197
+ return;
198
+ }
199
+ // Notification from server
200
+ if (msg.method === 'textDocument/publishDiagnostics') {
201
+ handleDiagnostics(msg.params);
202
+ }
203
+ }
204
+ function handleDiagnostics(params) {
205
+ const file = params.uri.replace('file://', '');
206
+ // Remove old diagnostics for this file
207
+ context.diagnostics = context.diagnostics.filter(d => d.file !== file);
208
+ // Add new diagnostics
209
+ for (const d of params.diagnostics) {
210
+ context.diagnostics.push({
211
+ file,
212
+ line: d.range.start.line + 1,
213
+ column: d.range.start.character + 1,
214
+ severity: mapSeverity(d.severity),
215
+ message: d.message,
216
+ source: d.source || 'lsp',
217
+ code: d.code !== undefined ? String(d.code) : undefined,
218
+ });
219
+ }
220
+ }
221
+ // ── Project Detection ──
222
+ function detectProjectServer(projectDir) {
223
+ // Check for project markers and pick the right server
224
+ const markers = [
225
+ { file: 'tsconfig.json', server: 'typescript' },
226
+ { file: 'jsconfig.json', server: 'typescript' },
227
+ { file: 'package.json', server: 'typescript' },
228
+ { file: 'pyproject.toml', server: 'python' },
229
+ { file: 'setup.py', server: 'python' },
230
+ { file: 'requirements.txt', server: 'python' },
231
+ { file: 'Cargo.toml', server: 'rust' },
232
+ { file: 'go.mod', server: 'go' },
233
+ { file: 'CMakeLists.txt', server: 'clangd' },
234
+ { file: 'Makefile', server: 'clangd' },
235
+ { file: 'pom.xml', server: 'java' },
236
+ { file: 'build.gradle', server: 'java' },
237
+ { file: 'Gemfile', server: 'ruby' },
238
+ { file: 'build.zig', server: 'zig' },
239
+ ];
240
+ for (const marker of markers) {
241
+ if (existsSync(join(projectDir, marker.file))) {
242
+ const config = LSP_SERVERS[marker.server];
243
+ if (config && isBinaryAvailable(config.detectBinary)) {
244
+ return config;
245
+ }
246
+ // If primary not available, try fallback (e.g., pylsp for python)
247
+ if (marker.server === 'python') {
248
+ const fallback = LSP_SERVERS['python-pylsp'];
249
+ if (fallback && isBinaryAvailable(fallback.detectBinary)) {
250
+ return fallback;
251
+ }
252
+ }
253
+ }
254
+ }
255
+ // Fallback: scan file extensions
256
+ try {
257
+ const entries = readdirSync(projectDir).slice(0, 100);
258
+ const extCounts = {};
259
+ for (const entry of entries) {
260
+ const ext = extname(entry).toLowerCase();
261
+ if (ext)
262
+ extCounts[ext] = (extCounts[ext] || 0) + 1;
263
+ }
264
+ // Find the most common extension that has an LSP server
265
+ const sorted = Object.entries(extCounts).sort((a, b) => b[1] - a[1]);
266
+ for (const [ext] of sorted) {
267
+ for (const config of Object.values(LSP_SERVERS)) {
268
+ if (config.extensions.includes(ext) && isBinaryAvailable(config.detectBinary)) {
269
+ return config;
270
+ }
271
+ }
272
+ }
273
+ }
274
+ catch { /* ignore */ }
275
+ return null;
276
+ }
277
+ function isBinaryAvailable(binary) {
278
+ try {
279
+ execSync(`which ${binary}`, { stdio: 'ignore', timeout: 3000 });
280
+ return true;
281
+ }
282
+ catch {
283
+ return false;
284
+ }
285
+ }
286
+ // ── Public API ──
287
+ /**
288
+ * Attach to the appropriate language server for the project.
289
+ *
290
+ * Detects the project type, spawns the right LSP server, initializes
291
+ * the protocol, and begins monitoring diagnostics. Returns the live
292
+ * LSPContext that updates as the server reports new information.
293
+ */
294
+ export async function attachLSP(projectDir) {
295
+ const absDir = resolve(projectDir);
296
+ // Detach existing session if any
297
+ if (activeServer) {
298
+ detachLSP();
299
+ }
300
+ const serverConfig = detectProjectServer(absDir);
301
+ if (!serverConfig) {
302
+ if (process.env.KBOT_DEBUG) {
303
+ process.stderr.write('[lsp-deep] no suitable language server found\n');
304
+ }
305
+ return context;
306
+ }
307
+ activeConfig = serverConfig;
308
+ activeProjectDir = absDir;
309
+ messageId = 0;
310
+ buffer = '';
311
+ initialized = false;
312
+ context.diagnostics = [];
313
+ context.symbols = [];
314
+ context.references.clear();
315
+ context.typeInfo.clear();
316
+ context.serverCapabilities = {};
317
+ // Spawn the server
318
+ try {
319
+ activeServer = spawn(serverConfig.command[0], serverConfig.command.slice(1), {
320
+ stdio: ['pipe', 'pipe', 'pipe'],
321
+ cwd: absDir,
322
+ });
323
+ }
324
+ catch (err) {
325
+ if (process.env.KBOT_DEBUG) {
326
+ process.stderr.write(`[lsp-deep] spawn failed: ${err.message}\n`);
327
+ }
328
+ return context;
329
+ }
330
+ activeServer.on('error', (err) => {
331
+ if (process.env.KBOT_DEBUG) {
332
+ process.stderr.write(`[lsp-deep] server error: ${err.message}\n`);
333
+ }
334
+ });
335
+ activeServer.on('exit', (code) => {
336
+ if (process.env.KBOT_DEBUG) {
337
+ process.stderr.write(`[lsp-deep] server exited with code ${code}\n`);
338
+ }
339
+ activeServer = null;
340
+ initialized = false;
341
+ });
342
+ // Handle incoming messages
343
+ activeServer.stdout?.on('data', (chunk) => {
344
+ buffer += chunk.toString();
345
+ const { messages, remaining } = parseMessages(buffer);
346
+ buffer = remaining;
347
+ for (const msg of messages) {
348
+ handleMessage(msg);
349
+ }
350
+ });
351
+ // Initialize the LSP protocol
352
+ const rootUri = `file://${absDir}`;
353
+ try {
354
+ const initResult = await sendRequest('initialize', {
355
+ processId: process.pid,
356
+ capabilities: {
357
+ textDocument: {
358
+ publishDiagnostics: { relatedInformation: true, codeDescriptionSupport: true },
359
+ hover: { contentFormat: ['plaintext', 'markdown'] },
360
+ completion: { completionItem: { snippetSupport: false } },
361
+ definition: { linkSupport: false },
362
+ references: {},
363
+ documentSymbol: { hierarchicalDocumentSymbolSupport: true },
364
+ },
365
+ workspace: {
366
+ workspaceFolders: true,
367
+ },
368
+ },
369
+ rootUri,
370
+ workspaceFolders: [{ uri: rootUri, name: 'workspace' }],
371
+ }, 15000);
372
+ // Parse server capabilities
373
+ if (initResult && typeof initResult === 'object') {
374
+ const caps = initResult.capabilities || {};
375
+ context.serverCapabilities = {
376
+ hover: !!caps.hoverProvider,
377
+ definition: !!caps.definitionProvider,
378
+ references: !!caps.referencesProvider,
379
+ documentSymbol: !!caps.documentSymbolProvider,
380
+ completion: !!caps.completionProvider,
381
+ rename: !!caps.renameProvider,
382
+ codeAction: !!caps.codeActionProvider,
383
+ formatting: !!caps.documentFormattingProvider,
384
+ };
385
+ }
386
+ // Send initialized notification
387
+ sendNotification('initialized', {});
388
+ initialized = true;
389
+ }
390
+ catch (err) {
391
+ if (process.env.KBOT_DEBUG) {
392
+ process.stderr.write(`[lsp-deep] initialize failed: ${err.message}\n`);
393
+ }
394
+ detachLSP();
395
+ return context;
396
+ }
397
+ return context;
398
+ }
399
+ /**
400
+ * Get all current diagnostics, optionally filtered to errors/warnings only.
401
+ *
402
+ * Returns a snapshot of diagnostics from the LSP server. Use this
403
+ * to inject context into the agent prompt like:
404
+ * "There are 3 TypeScript errors in src/auth.ts"
405
+ */
406
+ export function getProactiveDiagnostics() {
407
+ return [...context.diagnostics];
408
+ }
409
+ /**
410
+ * Enrich file content with type information from the LSP.
411
+ *
412
+ * Adds inline type annotations as comments for the agent to understand
413
+ * the code better. Only annotates function signatures and variable
414
+ * declarations to avoid noise.
415
+ */
416
+ export function enrichFileContext(file, content) {
417
+ if (!initialized || !context.serverCapabilities.hover) {
418
+ return content;
419
+ }
420
+ const absPath = resolve(file);
421
+ const fileUri = `file://${absPath}`;
422
+ // Open the file in the LSP server
423
+ sendNotification('textDocument/didOpen', {
424
+ textDocument: {
425
+ uri: fileUri,
426
+ languageId: getLanguageId(absPath),
427
+ version: 1,
428
+ text: content,
429
+ },
430
+ });
431
+ // For synchronous enrichment, return the content with cached type info
432
+ const lines = content.split('\n');
433
+ const enrichedLines = [];
434
+ for (let i = 0; i < lines.length; i++) {
435
+ const line = lines[i];
436
+ enrichedLines.push(line);
437
+ // Check if we have cached type info for this line
438
+ const key = `${absPath}:${i + 1}`;
439
+ const typeHint = context.typeInfo.get(key);
440
+ if (typeHint) {
441
+ enrichedLines.push(` // [type] ${typeHint}`);
442
+ }
443
+ }
444
+ // Close the file
445
+ sendNotification('textDocument/didClose', {
446
+ textDocument: { uri: fileUri },
447
+ });
448
+ return enrichedLines.join('\n');
449
+ }
450
+ /**
451
+ * Get all references to a symbol at a given position.
452
+ *
453
+ * Use this before modifying a function/variable to understand the
454
+ * blast radius of the change.
455
+ */
456
+ export function getReferencesForSymbol(file, line, col) {
457
+ const cacheKey = `${file}:${line}:${col}`;
458
+ // Return cached references if available
459
+ if (context.references.has(cacheKey)) {
460
+ return context.references.get(cacheKey);
461
+ }
462
+ if (!initialized || !context.serverCapabilities.references) {
463
+ return [];
464
+ }
465
+ const absPath = resolve(file);
466
+ const fileUri = `file://${absPath}`;
467
+ // Fire the request — result will be cached when it arrives
468
+ sendRequest('textDocument/references', {
469
+ textDocument: { uri: fileUri },
470
+ position: { line: line - 1, character: col - 1 },
471
+ context: { includeDeclaration: true },
472
+ }, 8000).then((result) => {
473
+ if (Array.isArray(result)) {
474
+ const locations = result.map((ref) => ({
475
+ file: ref.uri.replace('file://', ''),
476
+ line: ref.range.start.line + 1,
477
+ column: ref.range.start.character + 1,
478
+ }));
479
+ context.references.set(cacheKey, locations);
480
+ }
481
+ }).catch(() => {
482
+ // Silently ignore reference lookup failures
483
+ });
484
+ return [];
485
+ }
486
+ /**
487
+ * Suggest a fix strategy for a diagnostic.
488
+ *
489
+ * Maps common diagnostic codes to actionable fix descriptions that
490
+ * the agent can use to auto-correct code.
491
+ */
492
+ export function suggestFix(diagnostic) {
493
+ const { code, message, source } = diagnostic;
494
+ // TypeScript common errors
495
+ if (source === 'ts' || source === 'typescript') {
496
+ const tsFixMap = {
497
+ '2304': `Import or declare the identifier: ${extractIdentifier(message)}`,
498
+ '2305': `The module does not export this member. Check the export name or use a different import.`,
499
+ '2307': `Module not found. Install the package or fix the import path.`,
500
+ '2322': `Type mismatch. Cast the value, update the type annotation, or fix the assigned value.`,
501
+ '2339': `Property does not exist on type. Add the property to the interface or use a type assertion.`,
502
+ '2345': `Argument type mismatch. Convert the argument or update the function signature.`,
503
+ '2551': `Property name typo. Did you mean a similarly-named property?`,
504
+ '2554': `Wrong number of arguments. Check the function signature.`,
505
+ '2571': `Object is of type 'unknown'. Add a type guard or type assertion.`,
506
+ '2741': `Missing property in object literal. Add the required property.`,
507
+ '6133': `Declared but never used. Remove the unused declaration or prefix with underscore.`,
508
+ '7006': `Parameter implicitly has 'any' type. Add an explicit type annotation.`,
509
+ '7016': `Could not find declaration file. Install @types/ package or add a .d.ts declaration.`,
510
+ '7031': `Binding element implicitly has 'any' type. Add type annotation to destructured parameter.`,
511
+ };
512
+ if (code && tsFixMap[code])
513
+ return tsFixMap[code];
514
+ }
515
+ // Python / Pyright common errors
516
+ if (source === 'pyright' || source === 'Pyright' || source === 'pylsp') {
517
+ if (message.includes('is not defined')) {
518
+ return `Import the symbol or define it before use.`;
519
+ }
520
+ if (message.includes('could not be resolved')) {
521
+ return `Install the package with pip/conda or fix the import path.`;
522
+ }
523
+ if (message.includes('is not assignable to type')) {
524
+ return `Type mismatch. Fix the assigned value or update the type hint.`;
525
+ }
526
+ if (message.includes('has no attribute')) {
527
+ return `Attribute does not exist. Check the object type or add the attribute.`;
528
+ }
529
+ if (message.includes('Missing return statement')) {
530
+ return `Add a return statement to the function or update the return type to None.`;
531
+ }
532
+ }
533
+ // Rust-analyzer common errors
534
+ if (source === 'rust-analyzer' || source === 'rustc') {
535
+ if (code === 'E0308')
536
+ return `Mismatched types. Convert with .into(), as, or fix the expression.`;
537
+ if (code === 'E0425')
538
+ return `Cannot find value. Import it or fix the identifier name.`;
539
+ if (code === 'E0433')
540
+ return `Failed to resolve path. Add a use statement or fix the module path.`;
541
+ if (code === 'E0382')
542
+ return `Value moved. Clone the value, borrow instead, or restructure ownership.`;
543
+ if (code === 'E0502')
544
+ return `Cannot borrow as mutable. Restructure to avoid overlapping borrows.`;
545
+ if (code === 'E0599')
546
+ return `No method found. Import the trait or check the type.`;
547
+ }
548
+ // Go (gopls) common errors
549
+ if (source === 'compiler' || source === 'gopls') {
550
+ if (message.includes('undefined:')) {
551
+ return `Identifier not defined. Import the package or declare the symbol.`;
552
+ }
553
+ if (message.includes('cannot use')) {
554
+ return `Type mismatch. Convert the value or fix the type.`;
555
+ }
556
+ if (message.includes('imported and not used')) {
557
+ return `Remove the unused import.`;
558
+ }
559
+ if (message.includes('declared and not used')) {
560
+ return `Use the variable or replace it with _.`;
561
+ }
562
+ }
563
+ // Generic fallbacks based on message patterns
564
+ if (/not found|cannot find|could not resolve|undefined/i.test(message)) {
565
+ return `Symbol or module not found. Check imports, spelling, and installed dependencies.`;
566
+ }
567
+ if (/type.*mismatch|not assignable|incompatible/i.test(message)) {
568
+ return `Type mismatch. Align the types between the source and target.`;
569
+ }
570
+ if (/unused|never read|not used/i.test(message)) {
571
+ return `Unused declaration. Remove it or prefix with underscore to suppress.`;
572
+ }
573
+ if (/missing|required/i.test(message)) {
574
+ return `Required element missing. Add the missing property, argument, or statement.`;
575
+ }
576
+ return null;
577
+ }
578
+ /** Extract an identifier name from a diagnostic message */
579
+ function extractIdentifier(message) {
580
+ const match = message.match(/'([^']+)'/);
581
+ return match ? match[1] : '(unknown)';
582
+ }
583
+ /**
584
+ * Detach from the language server and clean up resources.
585
+ */
586
+ export function detachLSP() {
587
+ // Cancel all pending requests
588
+ for (const [id, pending] of pendingRequests) {
589
+ clearTimeout(pending.timer);
590
+ pending.reject(new Error('LSP detached'));
591
+ pendingRequests.delete(id);
592
+ }
593
+ // Stop file watcher
594
+ if (fileWatcher) {
595
+ fileWatcher.close();
596
+ fileWatcher = null;
597
+ }
598
+ // Shut down server gracefully
599
+ if (activeServer) {
600
+ try {
601
+ activeServer.stdin?.write(encodeMessage({
602
+ jsonrpc: '2.0',
603
+ id: ++messageId,
604
+ method: 'shutdown',
605
+ params: null,
606
+ }));
607
+ const server = activeServer;
608
+ setTimeout(() => {
609
+ try {
610
+ server.stdin?.write(encodeMessage({
611
+ jsonrpc: '2.0',
612
+ method: 'exit',
613
+ params: null,
614
+ }));
615
+ server.kill();
616
+ }
617
+ catch { /* already dead */ }
618
+ }, 500);
619
+ }
620
+ catch {
621
+ activeServer.kill();
622
+ }
623
+ }
624
+ activeServer = null;
625
+ activeConfig = null;
626
+ activeProjectDir = null;
627
+ initialized = false;
628
+ buffer = '';
629
+ // Clear context
630
+ context.diagnostics = [];
631
+ context.symbols = [];
632
+ context.references.clear();
633
+ context.typeInfo.clear();
634
+ context.serverCapabilities = {};
635
+ }
636
+ // ── Formatting Helpers ──
637
+ /**
638
+ * Format diagnostics as a concise summary for agent context injection.
639
+ *
640
+ * Example output:
641
+ * "3 errors, 2 warnings in workspace:
642
+ * ERROR src/auth.ts:42:5 — Property 'token' does not exist on type 'User'
643
+ * ERROR src/auth.ts:58:12 — Cannot find name 'jwt'
644
+ * ERROR src/server.ts:15:1 — Module '"./db"' has no exported member 'connect'"
645
+ */
646
+ export function formatDiagnosticsSummary(diagnostics) {
647
+ const diags = diagnostics || context.diagnostics;
648
+ if (diags.length === 0)
649
+ return 'No LSP diagnostics.';
650
+ const errors = diags.filter(d => d.severity === 'error');
651
+ const warnings = diags.filter(d => d.severity === 'warning');
652
+ const infos = diags.filter(d => d.severity === 'info' || d.severity === 'hint');
653
+ const parts = [];
654
+ if (errors.length > 0)
655
+ parts.push(`${errors.length} error${errors.length === 1 ? '' : 's'}`);
656
+ if (warnings.length > 0)
657
+ parts.push(`${warnings.length} warning${warnings.length === 1 ? '' : 's'}`);
658
+ if (infos.length > 0)
659
+ parts.push(`${infos.length} info`);
660
+ const lines = [`${parts.join(', ')} in workspace:`];
661
+ // Show errors first (limit to 10 to avoid noise)
662
+ const shownDiags = [...errors, ...warnings, ...infos].slice(0, 10);
663
+ for (const d of shownDiags) {
664
+ const tag = d.severity.toUpperCase();
665
+ const relFile = activeProjectDir ? relative(activeProjectDir, d.file) : d.file;
666
+ lines.push(` ${tag} ${relFile}:${d.line}:${d.column} — ${d.message}`);
667
+ }
668
+ if (diags.length > 10) {
669
+ lines.push(` ... and ${diags.length - 10} more`);
670
+ }
671
+ return lines.join('\n');
672
+ }
673
+ /**
674
+ * Format reference locations for display.
675
+ */
676
+ export function formatReferences(locations) {
677
+ if (locations.length === 0)
678
+ return 'No references found.';
679
+ const lines = [`${locations.length} reference${locations.length === 1 ? '' : 's'}:`];
680
+ for (const loc of locations.slice(0, 20)) {
681
+ const relFile = activeProjectDir ? relative(activeProjectDir, loc.file) : loc.file;
682
+ lines.push(` ${relFile}:${loc.line}:${loc.column}`);
683
+ }
684
+ if (locations.length > 20) {
685
+ lines.push(` ... and ${locations.length - 20} more`);
686
+ }
687
+ return lines.join('\n');
688
+ }
689
+ //# sourceMappingURL=lsp-deep.js.map