@botconnector/bccli 0.4.2 → 0.4.4

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 (29) hide show
  1. package/dist/{acp-UYC6APR5.js → acp-SWBX4TT6.js} +2 -2
  2. package/dist/{args-NRTZWWEV.js → args-UCLW7NID.js} +2 -2
  3. package/dist/{chunk-TIMSYZIR.js → chunk-5BVC3Y6B.js} +2 -2
  4. package/dist/{chunk-266SY3MC.js → chunk-CGBSE55G.js} +1 -1
  5. package/dist/{chunk-R72OV7RF.js → chunk-FI7RUJRX.js} +255 -71
  6. package/dist/{chunk-ZEPTUKNW.js → chunk-HBQ6OLQT.js} +76 -20
  7. package/dist/{chunk-5YJ3ZQVR.js → chunk-MSDK4IIJ.js} +9 -7
  8. package/dist/{chunk-IYR5PYA3.js → chunk-OZ5GHMTN.js} +1 -1
  9. package/dist/{chunk-KPTML72Q.js → chunk-QQJ2VVIW.js} +8 -0
  10. package/dist/{chunk-H7XJDKJY.js → chunk-RZF6A43N.js} +1 -1
  11. package/dist/{chunk-SVD4LHJC.js → chunk-SMTBYDNO.js} +1 -1
  12. package/dist/{chunk-6UO5GX55.js → chunk-TGGKACSE.js} +1 -1
  13. package/dist/{chunk-C64KM37J.js → chunk-YCHCHOMK.js} +5 -1
  14. package/dist/cli.js +22 -22
  15. package/dist/{ghBotCli-TSLRORJJ.js → ghBotCli-ZVWU7EMG.js} +8 -8
  16. package/dist/{login-RYESFZG6.js → login-QPUWIC76.js} +1 -1
  17. package/dist/lsp.d.ts +23 -0
  18. package/dist/{modelsCli-OP7CDZC7.js → modelsCli-SZK4E2BY.js} +3 -3
  19. package/dist/{providerCli-5OZI2GQQ.js → providerCli-PKI7B2SC.js} +4 -4
  20. package/dist/sdk.js +341 -92
  21. package/dist/{serve-4J7HA4JO.js → serve-L3SAIAAI.js} +9 -9
  22. package/dist/{session-YQXCCKXI.js → session-ASXO6GXR.js} +3 -3
  23. package/dist/{setup-S4AJT6XE.js → setup-F7FZ66OS.js} +7 -7
  24. package/dist/tools/diagnostics.d.ts +8 -0
  25. package/dist/tools/sandbox.d.ts +3 -0
  26. package/dist/{transferCli-R2KEIWQR.js → transferCli-HKGAWZ3Q.js} +3 -3
  27. package/dist/{ui-36ZEK65V.js → ui-NPA52YJH.js} +45 -9
  28. package/dist/{update-FSS7X5WV.js → update-AFIM34US.js} +1 -1
  29. package/package.json +1 -1
package/dist/sdk.js CHANGED
@@ -925,6 +925,14 @@ function reasoningCapability(providerId) {
925
925
  case "bc-cloud":
926
926
  return { levels: BOTCONNECTOR_LEVELS, parameter: "reasoning_effort" };
927
927
  case "openai":
928
+ case "gmi":
929
+ case "gmicloud":
930
+ case "vertex-maas":
931
+ case "deepseek":
932
+ case "groq":
933
+ case "together":
934
+ case "fireworks":
935
+ case "mistral":
928
936
  return { levels: OPENAI_LEVELS, parameter: "reasoning_effort" };
929
937
  case "openrouter":
930
938
  return { levels: ["auto", "low", "medium", "high"], parameter: "reasoning" };
@@ -1230,7 +1238,7 @@ async function readStream(res, onText, onThinking) {
1230
1238
  if (!choice) return;
1231
1239
  if (choice.finish_reason) finishReason = choice.finish_reason;
1232
1240
  const delta = choice.delta ?? {};
1233
- const reasoning = delta.reasoning_content || delta.reasoning;
1241
+ const reasoning = delta.reasoning_content || delta.reasoning || delta.thinking;
1234
1242
  if (reasoning) emit({ text: "", thinking: reasoning });
1235
1243
  if (delta.content) emit(splitter.push(delta.content));
1236
1244
  for (const tc of delta.tool_calls ?? []) {
@@ -1261,7 +1269,7 @@ async function readStream(res, onText, onThinking) {
1261
1269
  const toolCalls = calls.map((call, index) => ({ ...call, id: call.id || `call_${index}` }));
1262
1270
  return { text, toolCalls, thinking: thinking || void 0, usage, finishReason };
1263
1271
  }
1264
- async function readJson(res) {
1272
+ async function readJson(res, onText, onThinking) {
1265
1273
  const body = await res.json();
1266
1274
  const choice = body.choices?.[0];
1267
1275
  const toolCalls = (choice?.message?.tool_calls ?? []).map((tc, i) => ({
@@ -1270,7 +1278,9 @@ async function readJson(res) {
1270
1278
  arguments: tc.function?.arguments ?? ""
1271
1279
  }));
1272
1280
  const split = splitThinking(choice?.message?.content ?? "");
1273
- const thinking = (choice?.message?.reasoning_content || choice?.message?.reasoning || "") + split.thinking;
1281
+ const thinking = (choice?.message?.reasoning_content || choice?.message?.reasoning || choice?.message?.thinking || "") + split.thinking;
1282
+ if (thinking) onThinking?.(thinking);
1283
+ if (split.text) onText?.(split.text);
1274
1284
  return { text: split.text, toolCalls, thinking: thinking || void 0, usage: toUsage(body.usage), finishReason: choice?.finish_reason };
1275
1285
  }
1276
1286
  function contentText(content) {
@@ -1829,7 +1839,7 @@ function createProvider(options) {
1829
1839
  const isBotConnectorCloud = /^https:\/\/api\.botconnector\.id\/v1\/?$/.test(baseURL) && apiKey?.startsWith("bc_live_");
1830
1840
  if (isBotConnectorCloud) {
1831
1841
  headers["x-botconnector-client"] = "bccli";
1832
- headers["x-botconnector-client-version"] = "0.4.2";
1842
+ headers["x-botconnector-client-version"] = "0.4.4";
1833
1843
  }
1834
1844
  const useNativeResponses = isBotConnectorCloud && providerId === "bc-cloud" && model === "gpt-6-luna";
1835
1845
  const useResponsesWebSocket = useNativeResponses && Boolean(apiKey) && (fetchOverride === void 0 || responsesWebSocketFactory !== void 0);
@@ -1860,7 +1870,7 @@ function createProvider(options) {
1860
1870
  baseURL: baseURL.replace(/\/+$/, ""),
1861
1871
  defaultHeaders: {
1862
1872
  "x-botconnector-client": "bccli",
1863
- "x-botconnector-client-version": "0.4.2"
1873
+ "x-botconnector-client-version": "0.4.4"
1864
1874
  }
1865
1875
  });
1866
1876
  lunaWs = responsesWebSocketFactory ? responsesWebSocketFactory(client) : new ResponsesWS(client, { reconnect: null });
@@ -2013,7 +2023,7 @@ function createProvider(options) {
2013
2023
  { model, messages, stream: true, stream_options: { include_usage: true }, ...tools?.length ? { tools } : {}, ...reasoningOverride },
2014
2024
  signal
2015
2025
  );
2016
- if ((res.headers.get("content-type") ?? "").includes("application/json")) return readJson(res);
2026
+ if ((res.headers.get("content-type") ?? "").includes("application/json")) return readJson(res, onText, onThinking);
2017
2027
  if (!res.body) throw new ProviderError(t("Empty response from the provider"));
2018
2028
  return readStream(res, onText, onThinking);
2019
2029
  },
@@ -2268,6 +2278,11 @@ import { join as join2 } from "path";
2268
2278
  import { z } from "zod";
2269
2279
 
2270
2280
  // src/tools/sandbox.ts
2281
+ function winPathToWsl(winPath) {
2282
+ const normalized = winPath.replace(/\\/g, "/");
2283
+ const m = /^([A-Za-z]):\/(.*)$/.exec(normalized);
2284
+ return m ? `/mnt/${m[1].toLowerCase()}/${m[2]}` : normalized;
2285
+ }
2271
2286
  function darwinProfile(cwd, network) {
2272
2287
  const writable = [cwd, "/tmp", "/private/tmp", "/dev", "/var/folders"].map((p) => `(subpath "${p}")`).join(" ");
2273
2288
  return [
@@ -2311,6 +2326,33 @@ function sandboxArgv(command, opts) {
2311
2326
  command
2312
2327
  ];
2313
2328
  }
2329
+ if (opts.platform === "win32" && (opts.wslBwrap || process.env.BCCLI_WIN32_WSL_SANDBOX === "1")) {
2330
+ const wslCwd = winPathToWsl(opts.cwd);
2331
+ return [
2332
+ "wsl.exe",
2333
+ "--",
2334
+ "bwrap",
2335
+ "--ro-bind",
2336
+ "/",
2337
+ "/",
2338
+ "--dev-bind",
2339
+ "/dev",
2340
+ "/dev",
2341
+ "--proc",
2342
+ "/proc",
2343
+ "--bind",
2344
+ wslCwd,
2345
+ wslCwd,
2346
+ "--tmpfs",
2347
+ "/tmp",
2348
+ "--die-with-parent",
2349
+ ...opts.network ? [] : ["--unshare-net"],
2350
+ "--",
2351
+ "/bin/bash",
2352
+ "-c",
2353
+ command
2354
+ ];
2355
+ }
2314
2356
  return void 0;
2315
2357
  }
2316
2358
 
@@ -2393,10 +2435,14 @@ function startBackground(command, cwd, env, shell) {
2393
2435
  task.status = "exited";
2394
2436
  task.head += `${task.head ? "\n" : ""}${error.message}`;
2395
2437
  });
2396
- child.on("close", (code) => {
2438
+ child.on("exit", (code) => {
2397
2439
  task.status = "exited";
2398
2440
  task.exitCode = code;
2399
2441
  });
2442
+ child.on("close", (code) => {
2443
+ task.status = "exited";
2444
+ if (task.exitCode === null) task.exitCode = code;
2445
+ });
2400
2446
  return task;
2401
2447
  }
2402
2448
  function backgroundStatus(task) {
@@ -2490,8 +2536,40 @@ function runCommand(command, opts) {
2490
2536
  };
2491
2537
  child.stdout.on("data", onData);
2492
2538
  child.stderr.on("data", onData);
2539
+ let settled = false;
2540
+ let exitDrainTimer;
2541
+ let killDrainTimer;
2542
+ const finish = (exitCode, extra = "") => {
2543
+ if (settled) return;
2544
+ settled = true;
2545
+ active.delete(killAsAborted);
2546
+ clearTimeout(timer);
2547
+ if (exitDrainTimer) clearTimeout(exitDrainTimer);
2548
+ if (killDrainTimer) clearTimeout(killDrainTimer);
2549
+ opts.signal.removeEventListener("abort", onAbort);
2550
+ child.stdout.removeListener("data", onData);
2551
+ child.stderr.removeListener("data", onData);
2552
+ child.stdout.destroy();
2553
+ child.stderr.destroy();
2554
+ let spillNote = "";
2555
+ if (dropped && fullChunks.length) {
2556
+ try {
2557
+ const spillPath = join2(tmpdir(), `bccli-bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.log`);
2558
+ writeFileSync2(spillPath, fullChunks.join(""), "utf8");
2559
+ spillNote = `; full output saved to ${spillPath}`;
2560
+ } catch {
2561
+ }
2562
+ }
2563
+ const output = dropped ? `${head}
2564
+ \u2026 [${dropped} characters truncated${spillNote}] \u2026
2565
+ ${tail}` : head + tail;
2566
+ resolve5({ output: output + extra, exitCode, timedOut, aborted });
2567
+ };
2493
2568
  const kill = () => {
2494
2569
  if (child.pid !== void 0) killProcessTree(child.pid);
2570
+ if (!killDrainTimer) {
2571
+ killDrainTimer = setTimeout(() => finish(child.exitCode), 500);
2572
+ }
2495
2573
  };
2496
2574
  const killAsAborted = () => {
2497
2575
  aborted = true;
@@ -2508,29 +2586,16 @@ function runCommand(command, opts) {
2508
2586
  };
2509
2587
  if (opts.signal.aborted) onAbort();
2510
2588
  opts.signal.addEventListener("abort", onAbort, { once: true });
2511
- const finish = (exitCode, extra = "") => {
2512
- active.delete(killAsAborted);
2513
- clearTimeout(timer);
2514
- opts.signal.removeEventListener("abort", onAbort);
2515
- let spillNote = "";
2516
- if (dropped && fullChunks.length) {
2517
- try {
2518
- const spillPath = join2(tmpdir(), `bccli-bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.log`);
2519
- writeFileSync2(spillPath, fullChunks.join(""), "utf8");
2520
- spillNote = `; full output saved to ${spillPath}`;
2521
- } catch {
2522
- }
2589
+ child.on("exit", (code) => {
2590
+ if (!exitDrainTimer) {
2591
+ exitDrainTimer = setTimeout(() => finish(code), 200);
2523
2592
  }
2524
- const output = dropped ? `${head}
2525
- \u2026 [${dropped} characters truncated${spillNote}] \u2026
2526
- ${tail}` : head + tail;
2527
- resolve5({ output: output + extra, exitCode, timedOut, aborted });
2528
- };
2593
+ });
2529
2594
  child.on("error", (error) => finish(null, error.message));
2530
- child.on("close", (code) => finish(code));
2595
+ child.on("close", (code) => finish(code ?? child.exitCode));
2531
2596
  });
2532
2597
  }
2533
- var BASH_DESCRIPTION = "Run a shell command in the project directory. No interactive input. Default timeout 2 minutes (max 10). Use for tests, builds, git, and other CLI tasks. Set background: true for long-running jobs, then poll with task_id (stop with kill: true) or use the /tasks command.";
2598
+ var BASH_DESCRIPTION = "Run a shell command in the project directory. No interactive input. Default timeout 2 minutes (max 10). Use for tests, builds, git, and other CLI tasks. Set background: true for long-running jobs, then poll with task_id (stop with kill: true) or use the /tasks command. Do not pipe commands that start background daemons (such as agent-browser) through | head or | tail.";
2534
2599
  var BASH_SCHEMA = z.object({
2535
2600
  command: z.string().optional().describe("The shell command to run (required unless task_id is set)"),
2536
2601
  timeout_ms: z.number().int().min(1e3).max(6e5).optional().describe("Timeout in milliseconds"),
@@ -5476,14 +5541,14 @@ function createRepoMapTool() {
5476
5541
  }
5477
5542
 
5478
5543
  // src/tools/diagnostics.ts
5479
- import { extname as extname3 } from "path";
5544
+ import { extname as extname3, relative as relative3 } from "path";
5480
5545
  import { z as z17 } from "zod";
5481
5546
 
5482
5547
  // src/lsp.ts
5483
5548
  import { spawn as spawn4 } from "child_process";
5484
5549
  import { readFileSync as readFileSync12 } from "fs";
5485
5550
  import { extname as extname2 } from "path";
5486
- import { pathToFileURL } from "url";
5551
+ import { fileURLToPath, pathToFileURL } from "url";
5487
5552
  var LANGUAGE_IDS = {
5488
5553
  ".ts": "typescript",
5489
5554
  ".tsx": "typescriptreact",
@@ -5551,86 +5616,253 @@ function createLspParser(onMessage) {
5551
5616
  }
5552
5617
  };
5553
5618
  }
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)
5619
+ function extractHoverText(contents) {
5620
+ if (!contents) return "";
5621
+ if (typeof contents === "string") return contents.trim();
5622
+ if (Array.isArray(contents)) return contents.map(extractHoverText).filter(Boolean).join("\n\n");
5623
+ if (typeof contents === "object" && "value" in contents && typeof contents.value === "string") {
5624
+ return contents.value.trim();
5625
+ }
5626
+ return "";
5627
+ }
5628
+ function normalizeLocations(raw) {
5629
+ const items = Array.isArray(raw) ? raw : raw ? [raw] : [];
5630
+ const out = [];
5631
+ for (const item of items) {
5632
+ if (!item || typeof item !== "object") continue;
5633
+ const rec = item;
5634
+ const uri = rec.uri ?? rec.targetUri;
5635
+ const start = rec.range?.start ?? rec.targetSelectionRange?.start ?? rec.targetRange?.start;
5636
+ if (!uri) continue;
5637
+ let path = uri;
5638
+ try {
5639
+ if (uri.startsWith("file:")) path = fileURLToPath(uri);
5640
+ } catch {
5641
+ }
5642
+ out.push({
5643
+ uri,
5644
+ path,
5645
+ line: start?.line ?? 0,
5646
+ character: start?.character ?? 0
5560
5647
  });
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 {
5648
+ }
5649
+ return out;
5650
+ }
5651
+ function createWarmLspClient(opts) {
5652
+ let child;
5653
+ let initPromise;
5654
+ let nextId = 2;
5655
+ const opened = /* @__PURE__ */ new Map();
5656
+ const pendingRpc = /* @__PURE__ */ new Map();
5657
+ const diagWaiters = /* @__PURE__ */ new Map();
5658
+ const send = (msg) => {
5659
+ try {
5660
+ child?.stdin?.write(encodeLsp(msg));
5661
+ } catch {
5662
+ }
5663
+ };
5664
+ const resetState = () => {
5665
+ child = void 0;
5666
+ initPromise = void 0;
5667
+ opened.clear();
5668
+ for (const [, resolve5] of pendingRpc) resolve5(void 0);
5669
+ pendingRpc.clear();
5670
+ for (const [, waiters] of diagWaiters) {
5671
+ for (const w of waiters) w([]);
5672
+ }
5673
+ diagWaiters.clear();
5674
+ };
5675
+ const ensureStarted = () => {
5676
+ if (initPromise && child && !child.killed) return initPromise;
5677
+ opened.clear();
5678
+ initPromise = new Promise((resolveInit) => {
5679
+ const proc = spawn4(opts.server.command, opts.server.args ?? [], {
5680
+ cwd: opts.cwd,
5681
+ stdio: ["pipe", "pipe", "ignore"],
5682
+ shell: useShellForLsp(opts.server.command)
5683
+ });
5684
+ child = proc;
5685
+ let initSettled = false;
5686
+ const settleInit = () => {
5687
+ if (!initSettled) {
5688
+ initSettled = true;
5689
+ resolveInit();
5581
5690
  }
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 {
5691
+ };
5692
+ const parser = createLspParser((msg) => {
5693
+ if (msg.id === 1 && msg.result !== void 0) {
5694
+ send({ jsonrpc: "2.0", method: "initialized", params: {} });
5695
+ settleInit();
5696
+ return;
5597
5697
  }
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) => ({
5698
+ if (msg.id !== void 0 && pendingRpc.has(msg.id)) {
5699
+ const fn = pendingRpc.get(msg.id);
5700
+ pendingRpc.delete(msg.id);
5701
+ fn?.(msg.result);
5702
+ return;
5703
+ }
5704
+ if (msg.method === "textDocument/publishDiagnostics" && msg.params?.uri) {
5705
+ const uri = msg.params.uri;
5706
+ const diags = (msg.params.diagnostics ?? []).map((d) => ({
5607
5707
  line: d.range?.start?.line ?? 0,
5608
5708
  character: d.range?.start?.character ?? 0,
5609
5709
  severity: d.severity ?? 1,
5610
5710
  message: String(d.message ?? "")
5611
- }))
5612
- );
5613
- }
5711
+ }));
5712
+ const list2 = diagWaiters.get(uri);
5713
+ if (list2?.length) {
5714
+ diagWaiters.delete(uri);
5715
+ for (const w of list2) w(diags);
5716
+ }
5717
+ }
5718
+ });
5719
+ proc.stdout?.on("data", (c) => parser.push(c));
5720
+ proc.on("error", () => {
5721
+ resetState();
5722
+ settleInit();
5723
+ });
5724
+ proc.on("close", () => {
5725
+ resetState();
5726
+ settleInit();
5727
+ });
5728
+ send({
5729
+ jsonrpc: "2.0",
5730
+ id: 1,
5731
+ method: "initialize",
5732
+ params: { processId: process.pid, capabilities: {}, rootUri: pathToFileURL(opts.cwd).href }
5733
+ });
5614
5734
  });
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 }
5735
+ return initPromise;
5736
+ };
5737
+ const syncDocument = (file) => {
5738
+ const uri = pathToFileURL(file).href;
5739
+ let text = "";
5740
+ try {
5741
+ text = readFileSync12(file, "utf8");
5742
+ } catch {
5743
+ }
5744
+ const prevVersion = opened.get(uri);
5745
+ if (prevVersion === void 0) {
5746
+ opened.set(uri, 1);
5747
+ send({
5748
+ jsonrpc: "2.0",
5749
+ method: "textDocument/didOpen",
5750
+ params: { textDocument: { uri, languageId: languageIdFor(file), version: 1, text } }
5751
+ });
5752
+ } else {
5753
+ const version = prevVersion + 1;
5754
+ opened.set(uri, version);
5755
+ send({
5756
+ jsonrpc: "2.0",
5757
+ method: "textDocument/didChange",
5758
+ params: {
5759
+ textDocument: { uri, version },
5760
+ contentChanges: [{ text }]
5761
+ }
5762
+ });
5763
+ }
5764
+ return uri;
5765
+ };
5766
+ const request = (method, params, timeoutMs = opts.timeoutMs ?? 8e3) => new Promise((resolve5) => {
5767
+ const id = nextId++;
5768
+ const timer = setTimeout(() => {
5769
+ pendingRpc.delete(id);
5770
+ resolve5(void 0);
5771
+ }, timeoutMs);
5772
+ pendingRpc.set(id, (res) => {
5773
+ clearTimeout(timer);
5774
+ resolve5(res);
5623
5775
  });
5776
+ send({ jsonrpc: "2.0", id, method, params });
5624
5777
  });
5778
+ return {
5779
+ async diagnostics(file, timeoutMs = opts.timeoutMs ?? 8e3) {
5780
+ await ensureStarted();
5781
+ if (!child) return [];
5782
+ const uri = pathToFileURL(file).href;
5783
+ return new Promise((resolve5) => {
5784
+ const timer = setTimeout(() => {
5785
+ const list3 = diagWaiters.get(uri) ?? [];
5786
+ diagWaiters.set(
5787
+ uri,
5788
+ list3.filter((w) => w !== onDiag)
5789
+ );
5790
+ resolve5([]);
5791
+ }, timeoutMs);
5792
+ const onDiag = (diags) => {
5793
+ clearTimeout(timer);
5794
+ resolve5(diags);
5795
+ };
5796
+ const list2 = diagWaiters.get(uri) ?? [];
5797
+ list2.push(onDiag);
5798
+ diagWaiters.set(uri, list2);
5799
+ syncDocument(file);
5800
+ });
5801
+ },
5802
+ async hover(file, line, character, timeoutMs) {
5803
+ await ensureStarted();
5804
+ if (!child) return "";
5805
+ const uri = syncDocument(file);
5806
+ const res = await request("textDocument/hover", { textDocument: { uri }, position: { line, character } }, timeoutMs);
5807
+ return extractHoverText(res?.contents);
5808
+ },
5809
+ async definition(file, line, character, timeoutMs) {
5810
+ await ensureStarted();
5811
+ if (!child) return [];
5812
+ const uri = syncDocument(file);
5813
+ const res = await request("textDocument/definition", { textDocument: { uri }, position: { line, character } }, timeoutMs);
5814
+ return normalizeLocations(res);
5815
+ },
5816
+ async references(file, line, character, timeoutMs) {
5817
+ await ensureStarted();
5818
+ if (!child) return [];
5819
+ const uri = syncDocument(file);
5820
+ const res = await request(
5821
+ "textDocument/references",
5822
+ { textDocument: { uri }, position: { line, character }, context: { includeDeclaration: true } },
5823
+ timeoutMs
5824
+ );
5825
+ return normalizeLocations(res);
5826
+ },
5827
+ async close() {
5828
+ if (!child) return;
5829
+ const proc = child;
5830
+ send({ jsonrpc: "2.0", id: nextId++, method: "shutdown" });
5831
+ send({ jsonrpc: "2.0", method: "exit" });
5832
+ resetState();
5833
+ const killer = setTimeout(() => {
5834
+ try {
5835
+ proc.kill();
5836
+ } catch {
5837
+ }
5838
+ }, 200);
5839
+ killer.unref?.();
5840
+ }
5841
+ };
5625
5842
  }
5626
5843
 
5627
5844
  // src/tools/diagnostics.ts
5628
5845
  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)") });
5846
+ var SCHEMA = z17.object({
5847
+ path: z17.string().describe("File to check (absolute or relative to the working directory)"),
5848
+ action: z17.enum(["diagnostics", "hover", "definition", "references"]).optional().describe("LSP query action, default diagnostics"),
5849
+ line: z17.number().int().min(1).optional().describe("1-based line number for hover, definition, or references"),
5850
+ character: z17.number().int().min(1).optional().describe("1-based column number for hover, definition, or references")
5851
+ });
5630
5852
  function createDiagnosticsTool(servers) {
5853
+ const warmPool = /* @__PURE__ */ new Map();
5854
+ const clientFor = (server, cwd) => {
5855
+ const key = `${cwd}::${server.command}::${(server.args ?? []).join("\0")}`;
5856
+ let client = warmPool.get(key);
5857
+ if (!client) {
5858
+ client = createWarmLspClient({ server, cwd, timeoutMs: 15e3 });
5859
+ warmPool.set(key, client);
5860
+ }
5861
+ return client;
5862
+ };
5631
5863
  return defineTool({
5632
5864
  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"] }.',
5865
+ 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
5866
  schema: SCHEMA,
5635
5867
  kind: "read",
5636
5868
  target: (input) => input.path,
@@ -5641,7 +5873,24 @@ function createDiagnosticsTool(servers) {
5641
5873
  if (!server) {
5642
5874
  return { output: t("No LSP server is configured for {ext} files.", { ext: ext || "(none)" }), isError: true };
5643
5875
  }
5644
- const diags = await getDiagnostics({ server, cwd: ctx.cwd, file, timeoutMs: 15e3 });
5876
+ const client = clientFor(server, ctx.cwd);
5877
+ const action = input.action ?? "diagnostics";
5878
+ const line0 = Math.max(0, (input.line ?? 1) - 1);
5879
+ const char0 = Math.max(0, (input.character ?? 1) - 1);
5880
+ if (action === "hover") {
5881
+ const text = await client.hover(file, line0, char0, 15e3);
5882
+ return { output: text || "No hover information." };
5883
+ }
5884
+ if (action === "definition" || action === "references") {
5885
+ const locs = action === "definition" ? await client.definition(file, line0, char0, 15e3) : await client.references(file, line0, char0, 15e3);
5886
+ if (!locs.length) return { output: `No ${action} found.` };
5887
+ const formatted = locs.map((l) => {
5888
+ const rel = relative3(ctx.cwd, l.path).replace(/\\/g, "/") || input.path;
5889
+ return `${rel}:${l.line + 1}:${l.character + 1}`;
5890
+ });
5891
+ return { output: formatted.join("\n") };
5892
+ }
5893
+ const diags = await client.diagnostics(file, 15e3);
5645
5894
  if (!diags.length) return { output: "No diagnostics." };
5646
5895
  const lines = diags.map((d) => `${input.path}:${d.line + 1}:${d.character + 1} ${SEVERITY[d.severity] ?? "error"} ${d.message}`);
5647
5896
  return { output: lines.join("\n"), isError: diags.some((d) => d.severity === 1) };
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createRuntime
4
- } from "./chunk-R72OV7RF.js";
4
+ } from "./chunk-FI7RUJRX.js";
5
5
  import "./chunk-OK2USAXX.js";
6
- import "./chunk-ZEPTUKNW.js";
6
+ import "./chunk-HBQ6OLQT.js";
7
7
  import "./chunk-JXM6REER.js";
8
8
  import "./chunk-PSO7R5AD.js";
9
9
  import {
10
10
  VERSION
11
- } from "./chunk-266SY3MC.js";
11
+ } from "./chunk-CGBSE55G.js";
12
12
  import {
13
13
  LOOPBACK_BIND,
14
14
  MAX_FRAME_BYTES,
@@ -17,19 +17,19 @@ import {
17
17
  parseClientFrame,
18
18
  randomToken,
19
19
  trustFence
20
- } from "./chunk-6UO5GX55.js";
20
+ } from "./chunk-TGGKACSE.js";
21
21
  import "./chunk-WFZKJY3R.js";
22
- import "./chunk-H7XJDKJY.js";
22
+ import "./chunk-RZF6A43N.js";
23
23
  import {
24
24
  listAllModels
25
- } from "./chunk-IYR5PYA3.js";
25
+ } from "./chunk-OZ5GHMTN.js";
26
26
  import "./chunk-HQW4BROS.js";
27
27
  import "./chunk-3I5BW3ZT.js";
28
28
  import {
29
29
  Session
30
- } from "./chunk-SVD4LHJC.js";
31
- import "./chunk-5YJ3ZQVR.js";
32
- import "./chunk-KPTML72Q.js";
30
+ } from "./chunk-SMTBYDNO.js";
31
+ import "./chunk-MSDK4IIJ.js";
32
+ import "./chunk-QQJ2VVIW.js";
33
33
  import "./chunk-6M55UPES.js";
34
34
  import {
35
35
  bccliHome,
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  Session,
4
4
  pruneSessions
5
- } from "./chunk-SVD4LHJC.js";
6
- import "./chunk-5YJ3ZQVR.js";
7
- import "./chunk-KPTML72Q.js";
5
+ } from "./chunk-SMTBYDNO.js";
6
+ import "./chunk-MSDK4IIJ.js";
7
+ import "./chunk-QQJ2VVIW.js";
8
8
  import "./chunk-3WHI4LN3.js";
9
9
  export {
10
10
  Session,
@@ -2,19 +2,19 @@
2
2
  import {
3
3
  createRuntime,
4
4
  lunaPtcAutoEnabled
5
- } from "./chunk-R72OV7RF.js";
5
+ } from "./chunk-FI7RUJRX.js";
6
6
  import "./chunk-OK2USAXX.js";
7
- import "./chunk-ZEPTUKNW.js";
7
+ import "./chunk-HBQ6OLQT.js";
8
8
  import "./chunk-JXM6REER.js";
9
9
  import "./chunk-PSO7R5AD.js";
10
10
  import "./chunk-WFZKJY3R.js";
11
- import "./chunk-H7XJDKJY.js";
12
- import "./chunk-IYR5PYA3.js";
11
+ import "./chunk-RZF6A43N.js";
12
+ import "./chunk-OZ5GHMTN.js";
13
13
  import "./chunk-HQW4BROS.js";
14
14
  import "./chunk-3I5BW3ZT.js";
15
- import "./chunk-SVD4LHJC.js";
16
- import "./chunk-5YJ3ZQVR.js";
17
- import "./chunk-KPTML72Q.js";
15
+ import "./chunk-SMTBYDNO.js";
16
+ import "./chunk-MSDK4IIJ.js";
17
+ import "./chunk-QQJ2VVIW.js";
18
18
  import "./chunk-6M55UPES.js";
19
19
  import "./chunk-RX4UQX43.js";
20
20
  import "./chunk-3WHI4LN3.js";
@@ -2,4 +2,12 @@ import { z } from 'zod';
2
2
  import { type LspServer } from '../lsp';
3
3
  export declare function createDiagnosticsTool(servers: LspServer[]): import("./types").Tool<z.ZodObject<{
4
4
  path: z.ZodString;
5
+ action: z.ZodOptional<z.ZodEnum<{
6
+ definition: "definition";
7
+ diagnostics: "diagnostics";
8
+ hover: "hover";
9
+ references: "references";
10
+ }>>;
11
+ line: z.ZodOptional<z.ZodNumber>;
12
+ character: z.ZodOptional<z.ZodNumber>;
5
13
  }, z.core.$strip>>;