@botconnector/bccli 0.4.2 → 0.4.3

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/dist/cli.js CHANGED
@@ -3,36 +3,36 @@ import {
3
3
  resolvePrintPrompt,
4
4
  resolveWorktree,
5
5
  runPrint
6
- } from "./chunk-C64KM37J.js";
6
+ } from "./chunk-WDX62DFH.js";
7
7
  import {
8
8
  createRuntime
9
- } from "./chunk-R72OV7RF.js";
9
+ } from "./chunk-QNUJHKEL.js";
10
10
  import "./chunk-OK2USAXX.js";
11
- import "./chunk-ZEPTUKNW.js";
11
+ import "./chunk-4W5SKQDC.js";
12
12
  import "./chunk-JXM6REER.js";
13
13
  import "./chunk-PSO7R5AD.js";
14
14
  import {
15
15
  VERSION
16
- } from "./chunk-266SY3MC.js";
16
+ } from "./chunk-FUJGHCWN.js";
17
17
  import {
18
18
  helpText,
19
19
  parseCliArgs
20
20
  } from "./chunk-6UO5GX55.js";
21
21
  import "./chunk-WFZKJY3R.js";
22
- import "./chunk-H7XJDKJY.js";
23
- import "./chunk-IYR5PYA3.js";
22
+ import "./chunk-GWVLPN4B.js";
23
+ import "./chunk-7I2ZGPUS.js";
24
24
  import {
25
25
  readGlobalConfigFile
26
26
  } from "./chunk-HQW4BROS.js";
27
27
  import {
28
28
  defaultLoginDeps,
29
29
  runLogin
30
- } from "./chunk-TIMSYZIR.js";
30
+ } from "./chunk-Z5NI5FIK.js";
31
31
  import "./chunk-3I5BW3ZT.js";
32
32
  import {
33
33
  pruneSessions
34
- } from "./chunk-SVD4LHJC.js";
35
- import "./chunk-5YJ3ZQVR.js";
34
+ } from "./chunk-6AUROP2E.js";
35
+ import "./chunk-XTOATEI6.js";
36
36
  import "./chunk-KPTML72Q.js";
37
37
  import "./chunk-6M55UPES.js";
38
38
  import {
@@ -86,12 +86,12 @@ async function main() {
86
86
  if (args.command === "login") return runLogin(args.loginProvider ?? "bc-cloud", cwd, args, defaultLoginDeps());
87
87
  if (args.command === "mcp") {
88
88
  const { runMcpCommand } = await import("./mcpCli-SETTCL3K.js");
89
- const { readSecret } = await import("./login-RYESFZG6.js");
89
+ const { readSecret } = await import("./login-YXSSB2HQ.js");
90
90
  return runMcpCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
91
91
  }
92
92
  if (args.command === "provider") {
93
- const { runProviderCommand } = await import("./providerCli-5OZI2GQQ.js");
94
- const { readSecret } = await import("./login-RYESFZG6.js");
93
+ const { runProviderCommand } = await import("./providerCli-V2576JXE.js");
94
+ const { readSecret } = await import("./login-YXSSB2HQ.js");
95
95
  return runProviderCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
96
96
  }
97
97
  if (args.command === "connect" || args.command === "disconnect" || args.command === "integrations") {
@@ -105,20 +105,20 @@ async function main() {
105
105
  });
106
106
  }
107
107
  if (args.command === "models") {
108
- const { runModelsCommand } = await import("./modelsCli-OP7CDZC7.js");
108
+ const { runModelsCommand } = await import("./modelsCli-656AKYZD.js");
109
109
  return runModelsCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), fetch: globalThis.fetch });
110
110
  }
111
111
  if (args.command === "update") {
112
- const { runUpdateCommand } = await import("./update-FSS7X5WV.js");
112
+ const { runUpdateCommand } = await import("./update-AVZO4XID.js");
113
113
  return runUpdateCommand({ env: process.env });
114
114
  }
115
115
  if (args.command === "acp") {
116
- const { runtimeAgent, runAcp } = await import("./acp-UYC6APR5.js");
116
+ const { runtimeAgent, runAcp } = await import("./acp-NU4HZHQO.js");
117
117
  await runAcp(process.stdin, process.stdout, { cwd, newAgent: (c) => runtimeAgent(c, args.allowAll) });
118
118
  return 0;
119
119
  }
120
120
  if (args.command === "serve") {
121
- const { serveBanner, startServe } = await import("./serve-4J7HA4JO.js");
121
+ const { serveBanner, startServe } = await import("./serve-XPAGXIGS.js");
122
122
  const handle = await startServe({ port: args.port, host: args.host, token: args.token, cwd, env: process.env });
123
123
  process.stderr.write(serveBanner(handle, { token: args.token }));
124
124
  await new Promise((resolve) => {
@@ -146,7 +146,7 @@ async function main() {
146
146
  });
147
147
  }
148
148
  if (args.command === "gh-bot") {
149
- const { runGhBotCommand } = await import("./ghBotCli-TSLRORJJ.js");
149
+ const { runGhBotCommand } = await import("./ghBotCli-GCIUUMKQ.js");
150
150
  return runGhBotCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s) });
151
151
  }
152
152
  if (args.command === "completion") {
@@ -162,7 +162,7 @@ async function main() {
162
162
  );
163
163
  }
164
164
  if (args.command === "export" || args.command === "import") {
165
- const { runExportCommand, runImportCommand } = await import("./transferCli-R2KEIWQR.js");
165
+ const { runExportCommand, runImportCommand } = await import("./transferCli-C6XS6QYH.js");
166
166
  const { bccliHome } = await import("./config-LAINORU5.js");
167
167
  const deps = { subArgs: args.subArgs, home: bccliHome(process.env), cwd };
168
168
  const io = { out: (s) => console.log(s), err: (s) => console.error(s) };
@@ -215,13 +215,13 @@ async function main() {
215
215
  let updateMessage;
216
216
  if (rt.config.updateCheck !== "off" && !process.env.BCCLI_NO_UPDATE_CHECK) {
217
217
  try {
218
- const { checkForUpdate } = await import("./update-FSS7X5WV.js");
218
+ const { checkForUpdate } = await import("./update-AVZO4XID.js");
219
219
  const update = await checkForUpdate({ home: rt.home, fetch: globalThis.fetch, now: /* @__PURE__ */ new Date() });
220
220
  updateMessage = update;
221
221
  } catch {
222
222
  }
223
223
  }
224
- const { startInteractive } = await import("./ui-36ZEK65V.js");
224
+ const { startInteractive } = await import("./ui-43C6Y57K.js");
225
225
  return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION, updateMessage });
226
226
  }
227
227
  main().then(
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createRuntime
4
- } from "./chunk-R72OV7RF.js";
4
+ } from "./chunk-QNUJHKEL.js";
5
5
  import "./chunk-OK2USAXX.js";
6
- import "./chunk-ZEPTUKNW.js";
6
+ import "./chunk-4W5SKQDC.js";
7
7
  import "./chunk-JXM6REER.js";
8
8
  import "./chunk-PSO7R5AD.js";
9
9
  import {
@@ -11,12 +11,12 @@ import {
11
11
  parseCliArgs
12
12
  } from "./chunk-6UO5GX55.js";
13
13
  import "./chunk-WFZKJY3R.js";
14
- import "./chunk-H7XJDKJY.js";
15
- import "./chunk-IYR5PYA3.js";
14
+ import "./chunk-GWVLPN4B.js";
15
+ import "./chunk-7I2ZGPUS.js";
16
16
  import "./chunk-HQW4BROS.js";
17
17
  import "./chunk-3I5BW3ZT.js";
18
- import "./chunk-SVD4LHJC.js";
19
- import "./chunk-5YJ3ZQVR.js";
18
+ import "./chunk-6AUROP2E.js";
19
+ import "./chunk-XTOATEI6.js";
20
20
  import "./chunk-KPTML72Q.js";
21
21
  import "./chunk-6M55UPES.js";
22
22
  import {
@@ -4,7 +4,7 @@ import {
4
4
  readLine,
5
5
  readSecret,
6
6
  runLogin
7
- } from "./chunk-TIMSYZIR.js";
7
+ } from "./chunk-Z5NI5FIK.js";
8
8
  import "./chunk-3I5BW3ZT.js";
9
9
  import "./chunk-RX4UQX43.js";
10
10
  import "./chunk-3WHI4LN3.js";
package/dist/lsp.d.ts CHANGED
@@ -11,6 +11,19 @@ export interface LspDiagnostic {
11
11
  severity: number;
12
12
  message: string;
13
13
  }
14
+ export interface LspLocation {
15
+ uri: string;
16
+ path: string;
17
+ line: number;
18
+ character: number;
19
+ }
20
+ export interface WarmLspClient {
21
+ diagnostics(file: string, timeoutMs?: number): Promise<LspDiagnostic[]>;
22
+ hover(file: string, line: number, character: number, timeoutMs?: number): Promise<string>;
23
+ definition(file: string, line: number, character: number, timeoutMs?: number): Promise<LspLocation[]>;
24
+ references(file: string, line: number, character: number, timeoutMs?: number): Promise<LspLocation[]>;
25
+ close(): Promise<void>;
26
+ }
14
27
  interface LspRawDiagnostic {
15
28
  range?: {
16
29
  start?: {
@@ -47,4 +60,14 @@ export declare function getDiagnostics(opts: {
47
60
  timeoutMs?: number;
48
61
  signal?: AbortSignal;
49
62
  }): Promise<LspDiagnostic[]>;
63
+ /**
64
+ * Persistent warm LSP client: spawns the language server once, sends `didOpen` on first
65
+ * touch of a file and `didChange` on subsequent touches, and serves `diagnostics`,
66
+ * `hover`, `definition`, and `references` over the same warm process.
67
+ */
68
+ export declare function createWarmLspClient(opts: {
69
+ server: LspServer;
70
+ cwd: string;
71
+ timeoutMs?: number;
72
+ }): WarmLspClient;
50
73
  export {};
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listAllModels
4
- } from "./chunk-IYR5PYA3.js";
4
+ } from "./chunk-7I2ZGPUS.js";
5
5
  import "./chunk-HQW4BROS.js";
6
- import "./chunk-5YJ3ZQVR.js";
6
+ import "./chunk-XTOATEI6.js";
7
7
  import "./chunk-KPTML72Q.js";
8
8
  import {
9
9
  loadConfig,
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  addProviderKey,
4
4
  runProviderCommand
5
- } from "./chunk-H7XJDKJY.js";
6
- import "./chunk-IYR5PYA3.js";
5
+ } from "./chunk-GWVLPN4B.js";
6
+ import "./chunk-7I2ZGPUS.js";
7
7
  import "./chunk-HQW4BROS.js";
8
- import "./chunk-5YJ3ZQVR.js";
8
+ import "./chunk-XTOATEI6.js";
9
9
  import "./chunk-KPTML72Q.js";
10
10
  import "./chunk-RX4UQX43.js";
11
11
  import "./chunk-3WHI4LN3.js";
package/dist/sdk.js CHANGED
@@ -1829,7 +1829,7 @@ function createProvider(options) {
1829
1829
  const isBotConnectorCloud = /^https:\/\/api\.botconnector\.id\/v1\/?$/.test(baseURL) && apiKey?.startsWith("bc_live_");
1830
1830
  if (isBotConnectorCloud) {
1831
1831
  headers["x-botconnector-client"] = "bccli";
1832
- headers["x-botconnector-client-version"] = "0.4.2";
1832
+ headers["x-botconnector-client-version"] = "0.4.3";
1833
1833
  }
1834
1834
  const useNativeResponses = isBotConnectorCloud && providerId === "bc-cloud" && model === "gpt-6-luna";
1835
1835
  const useResponsesWebSocket = useNativeResponses && Boolean(apiKey) && (fetchOverride === void 0 || responsesWebSocketFactory !== void 0);
@@ -1860,7 +1860,7 @@ function createProvider(options) {
1860
1860
  baseURL: baseURL.replace(/\/+$/, ""),
1861
1861
  defaultHeaders: {
1862
1862
  "x-botconnector-client": "bccli",
1863
- "x-botconnector-client-version": "0.4.2"
1863
+ "x-botconnector-client-version": "0.4.3"
1864
1864
  }
1865
1865
  });
1866
1866
  lunaWs = responsesWebSocketFactory ? responsesWebSocketFactory(client) : new ResponsesWS(client, { reconnect: null });
@@ -2268,6 +2268,11 @@ import { join as join2 } from "path";
2268
2268
  import { z } from "zod";
2269
2269
 
2270
2270
  // src/tools/sandbox.ts
2271
+ function winPathToWsl(winPath) {
2272
+ const normalized = winPath.replace(/\\/g, "/");
2273
+ const m = /^([A-Za-z]):\/(.*)$/.exec(normalized);
2274
+ return m ? `/mnt/${m[1].toLowerCase()}/${m[2]}` : normalized;
2275
+ }
2271
2276
  function darwinProfile(cwd, network) {
2272
2277
  const writable = [cwd, "/tmp", "/private/tmp", "/dev", "/var/folders"].map((p) => `(subpath "${p}")`).join(" ");
2273
2278
  return [
@@ -2311,6 +2316,33 @@ function sandboxArgv(command, opts) {
2311
2316
  command
2312
2317
  ];
2313
2318
  }
2319
+ if (opts.platform === "win32" && (opts.wslBwrap || process.env.BCCLI_WIN32_WSL_SANDBOX === "1")) {
2320
+ const wslCwd = winPathToWsl(opts.cwd);
2321
+ return [
2322
+ "wsl.exe",
2323
+ "--",
2324
+ "bwrap",
2325
+ "--ro-bind",
2326
+ "/",
2327
+ "/",
2328
+ "--dev-bind",
2329
+ "/dev",
2330
+ "/dev",
2331
+ "--proc",
2332
+ "/proc",
2333
+ "--bind",
2334
+ wslCwd,
2335
+ wslCwd,
2336
+ "--tmpfs",
2337
+ "/tmp",
2338
+ "--die-with-parent",
2339
+ ...opts.network ? [] : ["--unshare-net"],
2340
+ "--",
2341
+ "/bin/bash",
2342
+ "-c",
2343
+ command
2344
+ ];
2345
+ }
2314
2346
  return void 0;
2315
2347
  }
2316
2348
 
@@ -5476,14 +5508,14 @@ function createRepoMapTool() {
5476
5508
  }
5477
5509
 
5478
5510
  // src/tools/diagnostics.ts
5479
- import { extname as extname3 } from "path";
5511
+ import { extname as extname3, relative as relative3 } from "path";
5480
5512
  import { z as z17 } from "zod";
5481
5513
 
5482
5514
  // src/lsp.ts
5483
5515
  import { spawn as spawn4 } from "child_process";
5484
5516
  import { readFileSync as readFileSync12 } from "fs";
5485
5517
  import { extname as extname2 } from "path";
5486
- import { pathToFileURL } from "url";
5518
+ import { fileURLToPath, pathToFileURL } from "url";
5487
5519
  var LANGUAGE_IDS = {
5488
5520
  ".ts": "typescript",
5489
5521
  ".tsx": "typescriptreact",
@@ -5551,86 +5583,253 @@ function createLspParser(onMessage) {
5551
5583
  }
5552
5584
  };
5553
5585
  }
5554
- function getDiagnostics(opts) {
5555
- return new Promise((resolve5) => {
5556
- const child = spawn4(opts.server.command, opts.server.args ?? [], {
5557
- cwd: opts.cwd,
5558
- stdio: ["pipe", "pipe", "ignore"],
5559
- shell: useShellForLsp(opts.server.command)
5586
+ function extractHoverText(contents) {
5587
+ if (!contents) return "";
5588
+ if (typeof contents === "string") return contents.trim();
5589
+ if (Array.isArray(contents)) return contents.map(extractHoverText).filter(Boolean).join("\n\n");
5590
+ if (typeof contents === "object" && "value" in contents && typeof contents.value === "string") {
5591
+ return contents.value.trim();
5592
+ }
5593
+ return "";
5594
+ }
5595
+ function normalizeLocations(raw) {
5596
+ const items = Array.isArray(raw) ? raw : raw ? [raw] : [];
5597
+ const out = [];
5598
+ for (const item of items) {
5599
+ if (!item || typeof item !== "object") continue;
5600
+ const rec = item;
5601
+ const uri = rec.uri ?? rec.targetUri;
5602
+ const start = rec.range?.start ?? rec.targetSelectionRange?.start ?? rec.targetRange?.start;
5603
+ if (!uri) continue;
5604
+ let path = uri;
5605
+ try {
5606
+ if (uri.startsWith("file:")) path = fileURLToPath(uri);
5607
+ } catch {
5608
+ }
5609
+ out.push({
5610
+ uri,
5611
+ path,
5612
+ line: start?.line ?? 0,
5613
+ character: start?.character ?? 0
5560
5614
  });
5561
- const uri = pathToFileURL(opts.file).href;
5562
- let settled = false;
5563
- let nextId = 2;
5564
- const send = (msg) => {
5565
- try {
5566
- child.stdin.write(encodeLsp(msg));
5567
- } catch {
5568
- }
5569
- };
5570
- const finish = (diags) => {
5571
- if (settled) return;
5572
- settled = true;
5573
- clearTimeout(timer);
5574
- opts.signal?.removeEventListener("abort", onAbort);
5575
- send({ jsonrpc: "2.0", id: nextId++, method: "shutdown" });
5576
- send({ jsonrpc: "2.0", method: "exit" });
5577
- const killer = setTimeout(() => {
5578
- try {
5579
- child.kill();
5580
- } catch {
5615
+ }
5616
+ return out;
5617
+ }
5618
+ function createWarmLspClient(opts) {
5619
+ let child;
5620
+ let initPromise;
5621
+ let nextId = 2;
5622
+ const opened = /* @__PURE__ */ new Map();
5623
+ const pendingRpc = /* @__PURE__ */ new Map();
5624
+ const diagWaiters = /* @__PURE__ */ new Map();
5625
+ const send = (msg) => {
5626
+ try {
5627
+ child?.stdin?.write(encodeLsp(msg));
5628
+ } catch {
5629
+ }
5630
+ };
5631
+ const resetState = () => {
5632
+ child = void 0;
5633
+ initPromise = void 0;
5634
+ opened.clear();
5635
+ for (const [, resolve5] of pendingRpc) resolve5(void 0);
5636
+ pendingRpc.clear();
5637
+ for (const [, waiters] of diagWaiters) {
5638
+ for (const w of waiters) w([]);
5639
+ }
5640
+ diagWaiters.clear();
5641
+ };
5642
+ const ensureStarted = () => {
5643
+ if (initPromise && child && !child.killed) return initPromise;
5644
+ opened.clear();
5645
+ initPromise = new Promise((resolveInit) => {
5646
+ const proc = spawn4(opts.server.command, opts.server.args ?? [], {
5647
+ cwd: opts.cwd,
5648
+ stdio: ["pipe", "pipe", "ignore"],
5649
+ shell: useShellForLsp(opts.server.command)
5650
+ });
5651
+ child = proc;
5652
+ let initSettled = false;
5653
+ const settleInit = () => {
5654
+ if (!initSettled) {
5655
+ initSettled = true;
5656
+ resolveInit();
5581
5657
  }
5582
- }, 500);
5583
- killer.unref?.();
5584
- resolve5(diags);
5585
- };
5586
- const onAbort = () => finish([]);
5587
- if (opts.signal?.aborted) return finish([]);
5588
- opts.signal?.addEventListener("abort", onAbort, { once: true });
5589
- const timer = setTimeout(() => finish([]), opts.timeoutMs ?? 8e3);
5590
- const parser = createLspParser((msg) => {
5591
- if (msg.id === 1 && msg.result) {
5592
- send({ jsonrpc: "2.0", method: "initialized", params: {} });
5593
- let text = "";
5594
- try {
5595
- text = readFileSync12(opts.file, "utf8");
5596
- } catch {
5658
+ };
5659
+ const parser = createLspParser((msg) => {
5660
+ if (msg.id === 1 && msg.result !== void 0) {
5661
+ send({ jsonrpc: "2.0", method: "initialized", params: {} });
5662
+ settleInit();
5663
+ return;
5597
5664
  }
5598
- send({
5599
- jsonrpc: "2.0",
5600
- method: "textDocument/didOpen",
5601
- params: { textDocument: { uri, languageId: languageIdFor(opts.file), version: 1, text } }
5602
- });
5603
- }
5604
- if (msg.method === "textDocument/publishDiagnostics" && msg.params?.uri === uri) {
5605
- finish(
5606
- (msg.params.diagnostics ?? []).map((d) => ({
5665
+ if (msg.id !== void 0 && pendingRpc.has(msg.id)) {
5666
+ const fn = pendingRpc.get(msg.id);
5667
+ pendingRpc.delete(msg.id);
5668
+ fn?.(msg.result);
5669
+ return;
5670
+ }
5671
+ if (msg.method === "textDocument/publishDiagnostics" && msg.params?.uri) {
5672
+ const uri = msg.params.uri;
5673
+ const diags = (msg.params.diagnostics ?? []).map((d) => ({
5607
5674
  line: d.range?.start?.line ?? 0,
5608
5675
  character: d.range?.start?.character ?? 0,
5609
5676
  severity: d.severity ?? 1,
5610
5677
  message: String(d.message ?? "")
5611
- }))
5612
- );
5613
- }
5678
+ }));
5679
+ const list2 = diagWaiters.get(uri);
5680
+ if (list2?.length) {
5681
+ diagWaiters.delete(uri);
5682
+ for (const w of list2) w(diags);
5683
+ }
5684
+ }
5685
+ });
5686
+ proc.stdout?.on("data", (c) => parser.push(c));
5687
+ proc.on("error", () => {
5688
+ resetState();
5689
+ settleInit();
5690
+ });
5691
+ proc.on("close", () => {
5692
+ resetState();
5693
+ settleInit();
5694
+ });
5695
+ send({
5696
+ jsonrpc: "2.0",
5697
+ id: 1,
5698
+ method: "initialize",
5699
+ params: { processId: process.pid, capabilities: {}, rootUri: pathToFileURL(opts.cwd).href }
5700
+ });
5614
5701
  });
5615
- child.stdout?.on("data", (c) => parser.push(c));
5616
- child.on("error", () => finish([]));
5617
- child.on("close", () => finish([]));
5618
- send({
5619
- jsonrpc: "2.0",
5620
- id: 1,
5621
- method: "initialize",
5622
- params: { processId: process.pid, capabilities: {}, rootUri: pathToFileURL(opts.cwd).href }
5702
+ return initPromise;
5703
+ };
5704
+ const syncDocument = (file) => {
5705
+ const uri = pathToFileURL(file).href;
5706
+ let text = "";
5707
+ try {
5708
+ text = readFileSync12(file, "utf8");
5709
+ } catch {
5710
+ }
5711
+ const prevVersion = opened.get(uri);
5712
+ if (prevVersion === void 0) {
5713
+ opened.set(uri, 1);
5714
+ send({
5715
+ jsonrpc: "2.0",
5716
+ method: "textDocument/didOpen",
5717
+ params: { textDocument: { uri, languageId: languageIdFor(file), version: 1, text } }
5718
+ });
5719
+ } else {
5720
+ const version = prevVersion + 1;
5721
+ opened.set(uri, version);
5722
+ send({
5723
+ jsonrpc: "2.0",
5724
+ method: "textDocument/didChange",
5725
+ params: {
5726
+ textDocument: { uri, version },
5727
+ contentChanges: [{ text }]
5728
+ }
5729
+ });
5730
+ }
5731
+ return uri;
5732
+ };
5733
+ const request = (method, params, timeoutMs = opts.timeoutMs ?? 8e3) => new Promise((resolve5) => {
5734
+ const id = nextId++;
5735
+ const timer = setTimeout(() => {
5736
+ pendingRpc.delete(id);
5737
+ resolve5(void 0);
5738
+ }, timeoutMs);
5739
+ pendingRpc.set(id, (res) => {
5740
+ clearTimeout(timer);
5741
+ resolve5(res);
5623
5742
  });
5743
+ send({ jsonrpc: "2.0", id, method, params });
5624
5744
  });
5745
+ return {
5746
+ async diagnostics(file, timeoutMs = opts.timeoutMs ?? 8e3) {
5747
+ await ensureStarted();
5748
+ if (!child) return [];
5749
+ const uri = pathToFileURL(file).href;
5750
+ return new Promise((resolve5) => {
5751
+ const timer = setTimeout(() => {
5752
+ const list3 = diagWaiters.get(uri) ?? [];
5753
+ diagWaiters.set(
5754
+ uri,
5755
+ list3.filter((w) => w !== onDiag)
5756
+ );
5757
+ resolve5([]);
5758
+ }, timeoutMs);
5759
+ const onDiag = (diags) => {
5760
+ clearTimeout(timer);
5761
+ resolve5(diags);
5762
+ };
5763
+ const list2 = diagWaiters.get(uri) ?? [];
5764
+ list2.push(onDiag);
5765
+ diagWaiters.set(uri, list2);
5766
+ syncDocument(file);
5767
+ });
5768
+ },
5769
+ async hover(file, line, character, timeoutMs) {
5770
+ await ensureStarted();
5771
+ if (!child) return "";
5772
+ const uri = syncDocument(file);
5773
+ const res = await request("textDocument/hover", { textDocument: { uri }, position: { line, character } }, timeoutMs);
5774
+ return extractHoverText(res?.contents);
5775
+ },
5776
+ async definition(file, line, character, timeoutMs) {
5777
+ await ensureStarted();
5778
+ if (!child) return [];
5779
+ const uri = syncDocument(file);
5780
+ const res = await request("textDocument/definition", { textDocument: { uri }, position: { line, character } }, timeoutMs);
5781
+ return normalizeLocations(res);
5782
+ },
5783
+ async references(file, line, character, timeoutMs) {
5784
+ await ensureStarted();
5785
+ if (!child) return [];
5786
+ const uri = syncDocument(file);
5787
+ const res = await request(
5788
+ "textDocument/references",
5789
+ { textDocument: { uri }, position: { line, character }, context: { includeDeclaration: true } },
5790
+ timeoutMs
5791
+ );
5792
+ return normalizeLocations(res);
5793
+ },
5794
+ async close() {
5795
+ if (!child) return;
5796
+ const proc = child;
5797
+ send({ jsonrpc: "2.0", id: nextId++, method: "shutdown" });
5798
+ send({ jsonrpc: "2.0", method: "exit" });
5799
+ resetState();
5800
+ const killer = setTimeout(() => {
5801
+ try {
5802
+ proc.kill();
5803
+ } catch {
5804
+ }
5805
+ }, 200);
5806
+ killer.unref?.();
5807
+ }
5808
+ };
5625
5809
  }
5626
5810
 
5627
5811
  // src/tools/diagnostics.ts
5628
5812
  var SEVERITY = { 1: "error", 2: "warning", 3: "info", 4: "hint" };
5629
- var SCHEMA = z17.object({ path: z17.string().describe("File to check (absolute or relative to the working directory)") });
5813
+ var SCHEMA = z17.object({
5814
+ path: z17.string().describe("File to check (absolute or relative to the working directory)"),
5815
+ action: z17.enum(["diagnostics", "hover", "definition", "references"]).optional().describe("LSP query action, default diagnostics"),
5816
+ line: z17.number().int().min(1).optional().describe("1-based line number for hover, definition, or references"),
5817
+ character: z17.number().int().min(1).optional().describe("1-based column number for hover, definition, or references")
5818
+ });
5630
5819
  function createDiagnosticsTool(servers) {
5820
+ const warmPool = /* @__PURE__ */ new Map();
5821
+ const clientFor = (server, cwd) => {
5822
+ const key = `${cwd}::${server.command}::${(server.args ?? []).join("\0")}`;
5823
+ let client = warmPool.get(key);
5824
+ if (!client) {
5825
+ client = createWarmLspClient({ server, cwd, timeoutMs: 15e3 });
5826
+ warmPool.set(key, client);
5827
+ }
5828
+ return client;
5829
+ };
5631
5830
  return defineTool({
5632
5831
  name: "diagnostics",
5633
- description: 'Run the configured LSP server(s) on one file and return its diagnostics (errors/warnings). Configure servers under "lsp.servers" in ~/.bccli/config.json, e.g. { "extensions": [".ts"], "command": "typescript-language-server", "args": ["--stdio"] }.',
5832
+ description: 'Query the configured LSP server(s) on a file for diagnostics (errors/warnings), hover docs, definition, or references. Configure servers under "lsp.servers" in ~/.bccli/config.json, e.g. { "extensions": [".ts"], "command": "typescript-language-server", "args": ["--stdio"] }.',
5634
5833
  schema: SCHEMA,
5635
5834
  kind: "read",
5636
5835
  target: (input) => input.path,
@@ -5641,7 +5840,24 @@ function createDiagnosticsTool(servers) {
5641
5840
  if (!server) {
5642
5841
  return { output: t("No LSP server is configured for {ext} files.", { ext: ext || "(none)" }), isError: true };
5643
5842
  }
5644
- const diags = await getDiagnostics({ server, cwd: ctx.cwd, file, timeoutMs: 15e3 });
5843
+ const client = clientFor(server, ctx.cwd);
5844
+ const action = input.action ?? "diagnostics";
5845
+ const line0 = Math.max(0, (input.line ?? 1) - 1);
5846
+ const char0 = Math.max(0, (input.character ?? 1) - 1);
5847
+ if (action === "hover") {
5848
+ const text = await client.hover(file, line0, char0, 15e3);
5849
+ return { output: text || "No hover information." };
5850
+ }
5851
+ if (action === "definition" || action === "references") {
5852
+ const locs = action === "definition" ? await client.definition(file, line0, char0, 15e3) : await client.references(file, line0, char0, 15e3);
5853
+ if (!locs.length) return { output: `No ${action} found.` };
5854
+ const formatted = locs.map((l) => {
5855
+ const rel = relative3(ctx.cwd, l.path).replace(/\\/g, "/") || input.path;
5856
+ return `${rel}:${l.line + 1}:${l.character + 1}`;
5857
+ });
5858
+ return { output: formatted.join("\n") };
5859
+ }
5860
+ const diags = await client.diagnostics(file, 15e3);
5645
5861
  if (!diags.length) return { output: "No diagnostics." };
5646
5862
  const lines = diags.map((d) => `${input.path}:${d.line + 1}:${d.character + 1} ${SEVERITY[d.severity] ?? "error"} ${d.message}`);
5647
5863
  return { output: lines.join("\n"), isError: diags.some((d) => d.severity === 1) };