@mrciphersmith/keryx 0.2.117 → 0.2.118

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 (2) hide show
  1. package/dist/cli.js +512 -245
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -50252,7 +50252,7 @@ import path47 from "path";
50252
50252
  // package.json
50253
50253
  var package_default = {
50254
50254
  name: "@mrciphersmith/keryx",
50255
- version: "0.2.117",
50255
+ version: "0.2.118",
50256
50256
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
50257
50257
  private: false,
50258
50258
  publishConfig: {
@@ -74138,6 +74138,15 @@ function parseGrokToml(file, text) {
74138
74138
  }
74139
74139
  continue;
74140
74140
  }
74141
+ const arrayHeader = /^\[\[([^[\]]+)\]\]$/.exec(line);
74142
+ if (arrayHeader !== null) {
74143
+ const first = arrayHeader[1].split(".")[0]?.trim().replace(/^(["'])(.*)\1$/, "$2");
74144
+ current = undefined;
74145
+ if (first === "mcp_servers") {
74146
+ problems.push({ file, message: `line ${lineNo}: "${line}" is not a table header this reader understands` });
74147
+ }
74148
+ continue;
74149
+ }
74141
74150
  if (line.startsWith("[")) {
74142
74151
  problems.push({ file, message: `line ${lineNo}: "${line}" is not a table header this reader understands` });
74143
74152
  current = undefined;
@@ -89194,6 +89203,7 @@ function resolveInitialTab(tabs, initialTab) {
89194
89203
  }
89195
89204
  return first.id;
89196
89205
  }
89206
+ var MODAL_BODY_SCROLLBAR_COLS = 1;
89197
89207
  function innerWidthOf(state) {
89198
89208
  const panelWidth = state.panel.width;
89199
89209
  if (typeof panelWidth === "number" && panelWidth > MODAL_PANEL_CHROME_X) {
@@ -89246,7 +89256,7 @@ function mountTab(state, input, tabId) {
89246
89256
  const size = resolveModalPanelSize(availWidth, state.chrome.renderer.height, input.contentRows);
89247
89257
  const panelWidth = Math.min(availWidth, size.width);
89248
89258
  const cleanup = input.renderTab(tabId, state.body, {
89249
- width: resolveModalInnerWidth(panelWidth),
89259
+ width: resolveModalInnerWidth(panelWidth) - MODAL_BODY_SCROLLBAR_COLS,
89250
89260
  height: modalBodyRows(size.height)
89251
89261
  });
89252
89262
  state.tabCleanup = typeof cleanup === "function" ? cleanup : undefined;
@@ -89423,6 +89433,9 @@ function ensureHost(otui, chrome) {
89423
89433
  if (key.name === "right" && claimArrow("right")) {
89424
89434
  return;
89425
89435
  }
89436
+ if (state.tabs.length < 2 && (key.name === "left" || key.name === "right")) {
89437
+ return;
89438
+ }
89426
89439
  if (key.name === "left" || onStrip && key.name === "tab" && key.shift === true) {
89427
89440
  const prev = idx > 0 ? state.tabs[idx - 1] : undefined;
89428
89441
  if (prev !== undefined) {
@@ -89464,11 +89477,11 @@ function ensureHost(otui, chrome) {
89464
89477
  }
89465
89478
  function makeHandle(state, generation) {
89466
89479
  return {
89467
- close() {
89480
+ close(opts) {
89468
89481
  if (state.generation !== generation) {
89469
89482
  return;
89470
89483
  }
89471
- closeHost(state, { restoreFocus: true, runOnClose: true });
89484
+ closeHost(state, { restoreFocus: opts?.restoreFocus ?? true, runOnClose: true });
89472
89485
  },
89473
89486
  setTab(id) {
89474
89487
  if (state.generation !== generation || !state.open || state.input === undefined) {
@@ -89517,6 +89530,12 @@ function openModal(otui, chrome, input) {
89517
89530
  if (focused === null || !containsNode(state.scroll, focused)) {
89518
89531
  state.tabStrip.focus();
89519
89532
  }
89533
+ const r = chrome.renderer;
89534
+ setTimeout(() => {
89535
+ if (state.generation === generation && state.open) {
89536
+ r.requestRender();
89537
+ }
89538
+ }, 0);
89520
89539
  return makeHandle(state, generation);
89521
89540
  }
89522
89541
 
@@ -90511,7 +90530,7 @@ function classifyBusyDispatch(params) {
90511
90530
  // src/tui/boot-animation.ts
90512
90531
  var DEFAULT_BOOT_DURATION_MS = 350;
90513
90532
  var WORDMARK = ["========================", " K E R Y X ", "========================"];
90514
- var STEPS = ["Reading .metaproject index", "Connecting provider", "Warming graph"];
90533
+ var SPLASH_HINT = "type a message to start \xB7 / for commands";
90515
90534
  async function playBootAnimation(otui, r, opts) {
90516
90535
  if (process.env.KERYX_SKIP_BOOT === "1" || opts.skip === true) {
90517
90536
  return;
@@ -90536,29 +90555,14 @@ async function playBootAnimation(otui, r, opts) {
90536
90555
  for (const [index, line] of WORDMARK.entries()) {
90537
90556
  logoBox.add(new otui.TextRenderable(r, { id: `boot-logo-${index}`, content: otui.t`${otui.bold(line)}` }));
90538
90557
  }
90539
- const statusText = new otui.TextRenderable(r, {
90540
- id: "boot-status",
90541
- content: otui.t`${otui.dim(STEPS[0] ?? "")}`,
90542
- marginTop: 1
90543
- });
90544
- box.add(statusText);
90545
90558
  box.add(new otui.TextRenderable(r, { id: "boot-hint", content: otui.t`${otui.dim("any key to skip")}`, marginTop: 1 }));
90546
90559
  await new Promise((resolve) => {
90547
90560
  let settled = false;
90548
- let stepIndex = 0;
90549
- const stepTimer = setInterval(() => {
90550
- stepIndex = Math.min(stepIndex + 1, STEPS.length - 1);
90551
- const step = STEPS[stepIndex];
90552
- if (step !== undefined) {
90553
- statusText.content = otui.t`${otui.dim(step)}`;
90554
- }
90555
- }, Math.max(1, Math.floor(durationMs / STEPS.length)));
90556
90561
  const finish = () => {
90557
90562
  if (settled) {
90558
90563
  return;
90559
90564
  }
90560
90565
  settled = true;
90561
- clearInterval(stepTimer);
90562
90566
  clearTimeout(doneTimer);
90563
90567
  unsubscribe();
90564
90568
  r.root.remove(box);
@@ -90568,6 +90572,31 @@ async function playBootAnimation(otui, r, opts) {
90568
90572
  const unsubscribe = opts.onKeypress(() => finish());
90569
90573
  });
90570
90574
  }
90575
+ var SHELL_CHROME_ROWS = 8;
90576
+ function mountEmptyTranscriptSplash(otui, r, transcript) {
90577
+ const rows = WORDMARK.length + 2;
90578
+ const paneRows = Math.max(0, r.height - SHELL_CHROME_ROWS);
90579
+ const box = new otui.BoxRenderable(r, {
90580
+ id: "transcript-splash",
90581
+ width: "100%",
90582
+ flexDirection: "column",
90583
+ alignItems: "center",
90584
+ flexShrink: 0,
90585
+ marginTop: Math.max(1, Math.floor((paneRows - rows) / 2))
90586
+ });
90587
+ for (const [index, line] of WORDMARK.entries()) {
90588
+ box.add(new otui.TextRenderable(r, { id: `splash-logo-${index}`, content: otui.t`${otui.bold(line)}` }));
90589
+ }
90590
+ box.add(new otui.TextRenderable(r, { id: "splash-hint", content: otui.t`${otui.dim(SPLASH_HINT)}`, marginTop: 1 }));
90591
+ transcript.add(box);
90592
+ let removed = false;
90593
+ return () => {
90594
+ if (removed)
90595
+ return;
90596
+ removed = true;
90597
+ transcript.remove(box);
90598
+ };
90599
+ }
90571
90600
 
90572
90601
  // src/tui/workspace-inspector.ts
90573
90602
  var WORKSPACE_COMMAND = "/workspace";
@@ -91306,13 +91335,11 @@ async function declineProposalViaShell(run, workspaceId, proposalId) {
91306
91335
 
91307
91336
  // src/tui/mcp-inspector.ts
91308
91337
  var MCP_INSPECTOR_FOOTER = [
91309
- { key: "\u2191/\u2193", label: "select" },
91310
- { key: "click", label: "row: select/act" },
91311
- { key: "c/d", label: "connect/disconnect" },
91312
- { key: "y", label: "confirm" },
91338
+ { key: "\u2191/\u2193", label: "scroll/select" },
91313
91339
  { key: "\u2190/\u2192", label: "tabs" },
91314
91340
  { key: "esc", label: "close" }
91315
91341
  ];
91342
+ var MCP_TAB_KEYS = "keys: c/d connect/disconnect \xB7 y confirm \xB7 click a row to arm, again to confirm";
91316
91343
  var MCP_TOOLS_COMMAND = "/integrations";
91317
91344
  function isMcpToolsCommand(line) {
91318
91345
  return (line.trim().split(/\s+/)[0] ?? "") === MCP_TOOLS_COMMAND;
@@ -91330,10 +91357,87 @@ var RUNTIME_LABELS = {
91330
91357
  function runtimeLabel(id) {
91331
91358
  return RUNTIME_LABELS[id] ?? id;
91332
91359
  }
91333
- function formatToolRowLine(tool) {
91334
- const risk = (tool.risk ?? "read").padEnd(6);
91335
- const name = tool.name.padEnd(28);
91336
- return `${name} ${risk} ${tool.description ?? ""}`.trimEnd();
91360
+ var MIN_HANGING_BUDGET = 24;
91361
+ var FALLBACK_INDENT = 4;
91362
+ function splitLongWord(word, budget) {
91363
+ const pieces = [];
91364
+ let rest = word;
91365
+ while (rest.length > budget) {
91366
+ const slash = rest.lastIndexOf("/", budget);
91367
+ const cut = slash > 0 ? slash : budget;
91368
+ pieces.push(rest.slice(0, cut));
91369
+ rest = rest.slice(cut);
91370
+ }
91371
+ pieces.push(rest);
91372
+ return pieces;
91373
+ }
91374
+ function wrapHangingRow(head, text, width, indent = head.length) {
91375
+ if (width === undefined) {
91376
+ return [`${head}${text}`.trimEnd()];
91377
+ }
91378
+ const hang = width - indent >= MIN_HANGING_BUDGET ? indent : Math.min(indent, FALLBACK_INDENT);
91379
+ const pad = " ".repeat(hang);
91380
+ const budget = Math.max(1, width - hang);
91381
+ const out = [];
91382
+ let current = head;
91383
+ let fresh = true;
91384
+ const units = [];
91385
+ for (const match of text.matchAll(/(\S+)( *)/g)) {
91386
+ const word = match[1];
91387
+ const last = units.at(-1);
91388
+ if (last !== undefined && last.gap === " " && last.word.includes("/") && word.includes("/") && !/^[/~-]/.test(word)) {
91389
+ last.word += ` ${word}`;
91390
+ last.gap = match[2];
91391
+ continue;
91392
+ }
91393
+ units.push({ word, gap: match[2] });
91394
+ }
91395
+ let gap = "";
91396
+ for (const unit of units) {
91397
+ for (const [i, piece] of splitLongWord(unit.word, budget).entries()) {
91398
+ const sep = fresh || i > 0 ? "" : gap;
91399
+ if (current.length + sep.length + piece.length <= width || fresh && current === pad) {
91400
+ current += sep + piece;
91401
+ } else {
91402
+ out.push(current.trimEnd());
91403
+ current = pad + piece;
91404
+ }
91405
+ fresh = false;
91406
+ }
91407
+ gap = unit.gap;
91408
+ }
91409
+ out.push(current.trimEnd());
91410
+ return out;
91411
+ }
91412
+ var TOOL_NAME_COLUMN = 28;
91413
+ var TOOL_APPROVAL_COLUMN = 8;
91414
+ function approvalLabel(risk) {
91415
+ const value = risk ?? "read";
91416
+ return value === "read" ? "none" : value;
91417
+ }
91418
+ var TOOLS_COLUMN_HEADER = `${"tool".padEnd(TOOL_NAME_COLUMN)} ${"approval".padEnd(TOOL_APPROVAL_COLUMN)} description`;
91419
+ function formatToolRowLines(tool, width) {
91420
+ const head = `${tool.name.padEnd(TOOL_NAME_COLUMN)} ${approvalLabel(tool.risk).padEnd(TOOL_APPROVAL_COLUMN)} `;
91421
+ return wrapHangingRow(head, tool.description ?? "", width, TOOL_NAME_COLUMN + TOOL_APPROVAL_COLUMN + 2);
91422
+ }
91423
+ function fitRowsByLines(lineCounts, start, rows) {
91424
+ if (lineCounts.length === 0) {
91425
+ return { start: 0, end: 0 };
91426
+ }
91427
+ let maxStart = lineCounts.length - 1;
91428
+ let tail = lineCounts[maxStart] ?? 1;
91429
+ while (maxStart > 0 && tail + (lineCounts[maxStart - 1] ?? 1) <= rows) {
91430
+ maxStart -= 1;
91431
+ tail += lineCounts[maxStart] ?? 1;
91432
+ }
91433
+ const from = Math.min(maxStart, Math.max(0, start));
91434
+ let end = from + 1;
91435
+ let used = lineCounts[from] ?? 1;
91436
+ while (end < lineCounts.length && used + (lineCounts[end] ?? 1) <= rows) {
91437
+ used += lineCounts[end] ?? 1;
91438
+ end += 1;
91439
+ }
91440
+ return { start: from, end };
91337
91441
  }
91338
91442
  function isActionable(id) {
91339
91443
  return id !== "generic";
@@ -91348,7 +91452,7 @@ function formatOtherServers(otherServers) {
91348
91452
  const list = rest > 0 ? `${shown.join(", ")}, +${rest} more` : shown.join(", ");
91349
91453
  return ` \xB7 also has: ${list}`;
91350
91454
  }
91351
- function formatMcpRowLine(runtime, isSelected, status) {
91455
+ function formatMcpRowLine(runtime, isSelected, status, width) {
91352
91456
  const mark = isSelected ? ">" : " ";
91353
91457
  const label = runtimeLabel(runtime.id).padEnd(20);
91354
91458
  const statusText = runtime.connected ? "\u25CF keryx connected" : "\u25CB keryx not connected";
@@ -91364,7 +91468,8 @@ function formatMcpRowLine(runtime, isSelected, status) {
91364
91468
  } else {
91365
91469
  action = runtime.connected ? " [d] disconnect" : " [c] connect";
91366
91470
  }
91367
- return `${mark} ${label} ${statusText}${action}${formatOtherServers(runtime.otherServers)}`;
91471
+ return wrapHangingRow(`${mark} ${label} `, `${statusText}${action}${formatOtherServers(runtime.otherServers)}`, width).join(`
91472
+ `);
91368
91473
  }
91369
91474
  function asRowTarget(body) {
91370
91475
  const parent = body;
@@ -91388,6 +91493,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91388
91493
  let rowCtor;
91389
91494
  let activeRenderer;
91390
91495
  let unsubscribeKey;
91496
+ let bodyWidth;
91391
91497
  const rendererHint = options.renderer ?? chrome?.renderer;
91392
91498
  const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
91393
91499
  const paintToolsRows = () => {
@@ -91400,11 +91506,23 @@ function presentMcpTools(openModal, otui, chrome, options) {
91400
91506
  toolsBody.add(new rowCtor(activeRenderer, { id: "mcp-tools-empty", content: "No tools available." }));
91401
91507
  return;
91402
91508
  }
91403
- const start = clampScroll3(toolsScroll, options.tools.length, bodyRows);
91404
- for (const [i, tool] of options.tools.slice(start, start + bodyRows).entries()) {
91405
- toolsBody.add(new rowCtor(activeRenderer, { id: `mcp-tool-row-${start + i}`, content: formatToolRowLine(tool) }));
91509
+ toolsBody.add(new rowCtor(activeRenderer, { id: "mcp-tools-columns", content: TOOLS_COLUMN_HEADER }));
91510
+ const rows = options.tools.map((tool) => formatToolRowLines(tool, bodyWidth));
91511
+ const window = toolsWindow(rows);
91512
+ toolsScroll = window.start;
91513
+ for (let index = window.start;index < window.end; index++) {
91514
+ toolsBody.add(new rowCtor(activeRenderer, {
91515
+ id: `mcp-tool-row-${index}`,
91516
+ content: (rows[index] ?? []).join(`
91517
+ `)
91518
+ }));
91406
91519
  }
91407
91520
  };
91521
+ const toolsRowBudget = () => {
91522
+ const captionLines = wrapHangingRow("", TOOLS_TAB_HEADER, bodyWidth, 0).length;
91523
+ return Math.max(1, bodyRows - captionLines - 1);
91524
+ };
91525
+ const toolsWindow = (rows) => fitRowsByLines(rows.map((lines) => lines.length), toolsScroll, toolsRowBudget());
91408
91526
  const paintMcpRows = () => {
91409
91527
  if (mcpBody === undefined || rowCtor === undefined) {
91410
91528
  return;
@@ -91416,18 +91534,19 @@ function presentMcpTools(openModal, otui, chrome, options) {
91416
91534
  mcpBody.add(new rowCtor(activeRenderer, { id: "mcp-mcp-empty", content: "No MCP client runtimes registered." }));
91417
91535
  return;
91418
91536
  }
91537
+ mcpBody.add(new rowCtor(activeRenderer, { id: "mcp-mcp-keys", content: MCP_TAB_KEYS }));
91419
91538
  const start = clampScroll3(mcpScroll, runtimes.length, bodyRows);
91420
91539
  for (const [i, runtime] of runtimes.slice(start, start + bodyRows).entries()) {
91421
91540
  const index = start + i;
91422
91541
  mcpBody.add(new rowCtor(activeRenderer, {
91423
91542
  id: `mcp-row-${runtime.id}`,
91424
- content: formatMcpRowLine(runtime, index === mcpSelected, status),
91543
+ content: formatMcpRowLine(runtime, index === mcpSelected, status, bodyWidth),
91425
91544
  onMouseDown: () => handleRowClick(runtime.id, index)
91426
91545
  }));
91427
91546
  }
91428
91547
  };
91429
91548
  const paint = () => {
91430
- toolsScroll = clampScroll3(toolsScroll, options.tools.length, bodyRows);
91549
+ toolsScroll = Math.max(0, toolsScroll);
91431
91550
  mcpScroll = scrollToReveal3(mcpSelected, mcpScroll, bodyRows);
91432
91551
  mcpScroll = clampScroll3(mcpScroll, runtimes.length, bodyRows);
91433
91552
  paintToolsRows();
@@ -91510,9 +91629,10 @@ function presentMcpTools(openModal, otui, chrome, options) {
91510
91629
  }
91511
91630
  rowCtor = ctor;
91512
91631
  activeRenderer = renderer;
91632
+ bodyWidth = ctx?.width;
91513
91633
  if (tabId === "tools") {
91514
91634
  toolsBody = target;
91515
- toolsScroll = clampScroll3(toolsScroll, options.tools.length, bodyRows);
91635
+ toolsScroll = Math.max(0, toolsScroll);
91516
91636
  paintToolsRows();
91517
91637
  return;
91518
91638
  }
@@ -91560,7 +91680,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91560
91680
  if (onMcp) {
91561
91681
  moveMcpSelection(mcpSelected - 1);
91562
91682
  } else {
91563
- toolsScroll = clampScroll3(toolsScroll - 1, options.tools.length, bodyRows);
91683
+ toolsScroll = Math.max(0, toolsScroll - 1);
91564
91684
  paint();
91565
91685
  }
91566
91686
  return;
@@ -91569,7 +91689,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91569
91689
  if (onMcp) {
91570
91690
  moveMcpSelection(mcpSelected + 1);
91571
91691
  } else {
91572
- toolsScroll = clampScroll3(toolsScroll + 1, options.tools.length, bodyRows);
91692
+ toolsScroll += 1;
91573
91693
  paint();
91574
91694
  }
91575
91695
  return;
@@ -91579,7 +91699,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91579
91699
  if (onMcp) {
91580
91700
  mcpScroll = clampScroll3(mcpScroll + step, runtimes.length, bodyRows);
91581
91701
  } else {
91582
- toolsScroll = clampScroll3(toolsScroll + step, options.tools.length, bodyRows);
91702
+ toolsScroll = Math.max(0, toolsScroll + step);
91583
91703
  }
91584
91704
  paint();
91585
91705
  }
@@ -91592,6 +91712,7 @@ function openMcpTools(otui, chrome, options) {
91592
91712
  }
91593
91713
 
91594
91714
  // src/tui/mcp-consumer.ts
91715
+ import { homedir as homedir9 } from "os";
91595
91716
  var MCP_CONSUMER_COMMAND = "/mcp";
91596
91717
  function isMcpConsumerCommand(line) {
91597
91718
  return (line.trim().split(/\s+/)[0] ?? "") === MCP_CONSUMER_COMMAND;
@@ -91620,12 +91741,21 @@ function credentialNames(server) {
91620
91741
  }
91621
91742
  return [...names].sort();
91622
91743
  }
91623
- function targetOf(server) {
91744
+ var MAX_TARGET_CHARS = 240;
91745
+ function collapseHome2(text, home) {
91746
+ const trimmed = home.replace(/\/+$/, "");
91747
+ if (trimmed === "")
91748
+ return text;
91749
+ const escaped = trimmed.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
91750
+ return text.replace(new RegExp(`(^|[\\s="'])${escaped}(?=/|$|[\\s"'])`, "g"), "$1~");
91751
+ }
91752
+ function targetOf(server, home) {
91624
91753
  const raw = server.raw;
91625
- const target = typeof raw.url === "string" && raw.url.length > 0 ? displayUrl(raw.url) : [raw.command, ...raw.args ?? []].filter(Boolean).join(" ");
91626
- return sanitiseIdentifier(target);
91754
+ const target = typeof raw.url === "string" && raw.url.length > 0 ? displayUrl(raw.url) : collapseHome2([raw.command, ...raw.args ?? []].filter(Boolean).join(" "), home);
91755
+ return sanitiseIdentifier(target, MAX_TARGET_CHARS);
91627
91756
  }
91628
91757
  function buildConsumerModel(input) {
91758
+ const home = input.home ?? homedir9();
91629
91759
  const stateByName = new Map(input.states.map((s) => [s.name, s]));
91630
91760
  const rows = input.configured.map((server) => {
91631
91761
  const state = stateByName.get(server.name);
@@ -91637,7 +91767,7 @@ function buildConsumerModel(input) {
91637
91767
  transport: transportOf(server),
91638
91768
  status,
91639
91769
  toolCount: state?.toolCount ?? 0,
91640
- target: targetOf(server),
91770
+ target: targetOf(server, home),
91641
91771
  credentials: credentialNames(server),
91642
91772
  detail,
91643
91773
  action: status === "needs-approval" ? `keryx mcp trust ${server.name}` : status === "needs_auth" ? "set the variable named above, then restart the session" : undefined
@@ -91645,10 +91775,10 @@ function buildConsumerModel(input) {
91645
91775
  });
91646
91776
  return {
91647
91777
  rows,
91648
- problems: input.problems.map((p) => `${p.file}: ${sanitiseForDisplay(p.message)}`),
91778
+ problems: input.problems.map((p) => `${collapseHome2(p.file, home)}: ${sanitiseForDisplay(p.message)}`),
91649
91779
  emptyHint: rows.length > 0 ? undefined : `No MCP servers configured.
91650
- user: ${input.userFile}
91651
- project: ${input.projectFile}`
91780
+ user: ${collapseHome2(input.userFile, home)}
91781
+ project: ${collapseHome2(input.projectFile, home)}`
91652
91782
  };
91653
91783
  }
91654
91784
  var MCP_CONSUMER_FOOTER = [
@@ -91677,7 +91807,7 @@ function consumerActionFor(status) {
91677
91807
  function statusGlyph(status) {
91678
91808
  return STATUS_GLYPH[status] ?? status;
91679
91809
  }
91680
- function formatConsumerModalRow(row, isSelected, status) {
91810
+ function formatConsumerModalRow(row, isSelected, status, width) {
91681
91811
  const mark = isSelected ? ">" : " ";
91682
91812
  const name = row.name.padEnd(18);
91683
91813
  const source = row.source.padEnd(10);
@@ -91702,7 +91832,8 @@ function formatConsumerModalRow(row, isSelected, status) {
91702
91832
  action = "";
91703
91833
  }
91704
91834
  const creds = row.credentials.length > 0 ? ` [reads ${row.credentials.join(", ")}]` : "";
91705
- return `${mark} ${name} ${source} ${transport} ${glyph}${tools}${action} ${row.target}${creds}`.trimEnd();
91835
+ return wrapHangingRow(`${mark} ${name} ${source} ${transport} ${glyph}`, `${tools}${action} ${row.target}${creds}`, width).join(`
91836
+ `);
91706
91837
  }
91707
91838
  function formatConsumerModalLines(model, selected, status) {
91708
91839
  if (model === undefined) {
@@ -91743,6 +91874,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
91743
91874
  let rowCtor;
91744
91875
  let activeRenderer;
91745
91876
  let unsubscribeKey;
91877
+ let bodyWidth;
91746
91878
  const rendererHint = options.renderer ?? chrome?.renderer;
91747
91879
  const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
91748
91880
  const rowsOf = () => options.snapshot()?.rows ?? [];
@@ -91775,7 +91907,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
91775
91907
  const index = start + i;
91776
91908
  body.add(new rowCtor(activeRenderer, {
91777
91909
  id: `mcp-consumer-row-${row.name}`,
91778
- content: formatConsumerModalRow(row, index === selected, status),
91910
+ content: formatConsumerModalRow(row, index === selected, status, bodyWidth),
91779
91911
  onMouseDown: () => handleRowClick(row.name, index)
91780
91912
  }));
91781
91913
  }
@@ -91846,6 +91978,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
91846
91978
  rowCtor = ctor;
91847
91979
  activeRenderer = renderer;
91848
91980
  body = target;
91981
+ bodyWidth = ctx?.width;
91849
91982
  paint();
91850
91983
  },
91851
91984
  onClose: () => {
@@ -97654,30 +97787,31 @@ var CUSTOM_ADD_ENTRY = {
97654
97787
  label: "\uFF0B Add custom provider (OpenAI-compatible)",
97655
97788
  note: "name \xB7 URL \xB7 key \xB7 models \u2192 saved to llm-providers.json"
97656
97789
  };
97657
- function promptCustomFieldStep(otui, r, opts) {
97790
+ function promptCustomFieldStep(otui, target, opts) {
97791
+ const r = stepRenderer(target);
97658
97792
  return new Promise((resolve) => {
97659
- const box = overlayBox(otui, r, "custom-field-picker");
97660
- r.root.add(box);
97661
- box.add(new otui.TextRenderable(r, { id: "cf-title", content: otui.t`${otui.bold(opts.title)} ${otui.dim("(Enter \xB7 Esc to go back)")}` }));
97793
+ const surface = openStepSurface(otui, target, {
97794
+ id: "custom-field-picker",
97795
+ title: opts.title,
97796
+ tab: "Custom provider",
97797
+ hint: "(Enter \xB7 Esc to go back)",
97798
+ footer: inputStepFooter("back"),
97799
+ contentRows: opts.note !== undefined ? 4 : 2,
97800
+ onEscape: () => {
97801
+ input.blur();
97802
+ resolve({ kind: "back" });
97803
+ }
97804
+ });
97662
97805
  if (opts.note !== undefined) {
97663
- box.add(new otui.TextRenderable(r, { id: "cf-note", content: otui.t`${otui.dim(opts.note)}`, marginTop: 1 }));
97806
+ surface.body.add(new otui.TextRenderable(r, { id: "cf-note", content: otui.t`${otui.dim(opts.note)}`, marginTop: 1 }));
97664
97807
  }
97665
97808
  const input = new otui.InputRenderable(r, { id: "cf-input", value: opts.value ?? "", marginTop: 1 });
97666
- box.add(input);
97809
+ surface.body.add(input);
97667
97810
  input.focus();
97668
97811
  const cleanup = () => {
97669
- unsub();
97670
97812
  input.blur();
97671
- r.root.remove(box);
97813
+ surface.close();
97672
97814
  };
97673
- const unsub = onKeypress4(r, (key) => {
97674
- if (key.name === "escape") {
97675
- cleanup();
97676
- resolve({ kind: "back" });
97677
- key.preventDefault();
97678
- key.stopPropagation();
97679
- }
97680
- });
97681
97815
  input.on(otui.InputRenderableEvents.ENTER, () => {
97682
97816
  const entered = input.value.trim();
97683
97817
  cleanup();
@@ -97697,9 +97831,9 @@ function parseOptionalPositiveWizardNumber(raw) {
97697
97831
  const parsed = parseOptionalWizardNumber(raw);
97698
97832
  return parsed !== undefined && parsed > 0 ? parsed : undefined;
97699
97833
  }
97700
- async function promptCustomProviderWizard(otui, r) {
97834
+ async function promptCustomProviderWizard(otui, target) {
97701
97835
  while (true) {
97702
- const nameStep = await promptCustomFieldStep(otui, r, {
97836
+ const nameStep = await promptCustomFieldStep(otui, target, {
97703
97837
  title: "Provider name",
97704
97838
  note: "unique id used in /provider (letters, digits, - _)"
97705
97839
  });
@@ -97711,7 +97845,7 @@ async function promptCustomProviderWizard(otui, r) {
97711
97845
  continue;
97712
97846
  }
97713
97847
  while (true) {
97714
- const urlStep = await promptCustomFieldStep(otui, r, {
97848
+ const urlStep = await promptCustomFieldStep(otui, target, {
97715
97849
  title: "Base URL",
97716
97850
  note: "before the chat path \u2014 /v1/chat/completions is appended, e.g. http://10.110.43.19:8080",
97717
97851
  value: "http://localhost:8000"
@@ -97733,7 +97867,7 @@ async function promptCustomProviderWizard(otui, r) {
97733
97867
  continue;
97734
97868
  }
97735
97869
  const baseUrl = baseUrlRaw.replace(/\/+$/, "");
97736
- const keyStep = await promptCustomFieldStep(otui, r, {
97870
+ const keyStep = await promptCustomFieldStep(otui, target, {
97737
97871
  title: "API key (optional)",
97738
97872
  note: "empty = no key \xB7 saved owner-only (0600) in your keryx config dir"
97739
97873
  });
@@ -97741,7 +97875,7 @@ async function promptCustomProviderWizard(otui, r) {
97741
97875
  continue;
97742
97876
  }
97743
97877
  const apiKey = keyStep.value.length > 0 ? keyStep.value : undefined;
97744
- const modelsStep = await promptCustomFieldStep(otui, r, {
97878
+ const modelsStep = await promptCustomFieldStep(otui, target, {
97745
97879
  title: "Models (optional)",
97746
97880
  note: "comma-separated ids \xB7 empty = fetch the live /v1/models list"
97747
97881
  });
@@ -97749,7 +97883,7 @@ async function promptCustomProviderWizard(otui, r) {
97749
97883
  continue;
97750
97884
  }
97751
97885
  const models = modelsStep.value.split(",").map((m) => m.trim()).filter((m) => m.length > 0);
97752
- const temperatureStep = await promptCustomFieldStep(otui, r, {
97886
+ const temperatureStep = await promptCustomFieldStep(otui, target, {
97753
97887
  title: "Temperature (optional)",
97754
97888
  note: "empty = provider default \xB7 e.g. 0.2"
97755
97889
  });
@@ -97757,7 +97891,7 @@ async function promptCustomProviderWizard(otui, r) {
97757
97891
  continue;
97758
97892
  }
97759
97893
  const temperature = parseOptionalWizardNumber(temperatureStep.value);
97760
- const maxOutputTokensStep = await promptCustomFieldStep(otui, r, {
97894
+ const maxOutputTokensStep = await promptCustomFieldStep(otui, target, {
97761
97895
  title: "Max output tokens (optional)",
97762
97896
  note: "empty = default 1024 \xB7 e.g. 4096"
97763
97897
  });
@@ -97765,7 +97899,7 @@ async function promptCustomProviderWizard(otui, r) {
97765
97899
  continue;
97766
97900
  }
97767
97901
  const maxOutputTokens = parseOptionalPositiveWizardNumber(maxOutputTokensStep.value);
97768
- const timeoutMsStep = await promptCustomFieldStep(otui, r, {
97902
+ const timeoutMsStep = await promptCustomFieldStep(otui, target, {
97769
97903
  title: "Request timeout ms (optional)",
97770
97904
  note: "empty = no engine-internal timeout \xB7 e.g. 180000"
97771
97905
  });
@@ -98120,6 +98254,78 @@ function onKeypress4(r, handler) {
98120
98254
  r._internalKeyInput.onInternal("keypress", handler);
98121
98255
  return () => r._internalKeyInput.offInternal("keypress", handler);
98122
98256
  }
98257
+ function isModalChrome(target) {
98258
+ return target !== null && typeof target === "object" && "renderer" in target && "focusComposer" in target;
98259
+ }
98260
+ function stepRenderer(target) {
98261
+ return isModalChrome(target) ? target.renderer : target;
98262
+ }
98263
+ function openStepSurface(otui, target, opts) {
98264
+ const r = stepRenderer(target);
98265
+ if (!isModalChrome(target)) {
98266
+ const box = overlayBox(otui, r, opts.id);
98267
+ r.root.add(box);
98268
+ box.add(new otui.TextRenderable(r, { id: `${opts.id}-title`, content: otui.t`${otui.bold(opts.title)} ${otui.dim(opts.hint)}` }));
98269
+ let closed = false;
98270
+ const close = () => {
98271
+ if (closed)
98272
+ return;
98273
+ closed = true;
98274
+ unsub();
98275
+ r.root.remove(box);
98276
+ };
98277
+ const unsub = onKeypress4(r, (key) => {
98278
+ if (key.name === "escape") {
98279
+ close();
98280
+ opts.onEscape();
98281
+ key.preventDefault();
98282
+ key.stopPropagation();
98283
+ }
98284
+ });
98285
+ return { body: box, modal: false, close };
98286
+ }
98287
+ let body;
98288
+ let closedByStep = false;
98289
+ const handle = openModal(otui, target, {
98290
+ title: opts.title,
98291
+ tabs: [{ id: opts.id, label: opts.tab }],
98292
+ footer: opts.footer,
98293
+ contentRows: opts.contentRows,
98294
+ renderTab: (_tabId, tabBody) => {
98295
+ body = tabBody;
98296
+ },
98297
+ onClose: () => {
98298
+ if (!closedByStep)
98299
+ opts.onEscape();
98300
+ }
98301
+ });
98302
+ if (handle === undefined || body === undefined) {
98303
+ return openStepSurface(otui, r, opts);
98304
+ }
98305
+ return {
98306
+ body,
98307
+ modal: true,
98308
+ close: () => {
98309
+ if (closedByStep)
98310
+ return;
98311
+ closedByStep = true;
98312
+ handle.close({ restoreFocus: false });
98313
+ }
98314
+ };
98315
+ }
98316
+ function selectStepFooter(escLabel) {
98317
+ return [
98318
+ { key: "\u2191/\u2193", label: "select" },
98319
+ { key: "Enter", label: "confirm" },
98320
+ { key: "esc", label: escLabel }
98321
+ ];
98322
+ }
98323
+ function inputStepFooter(escLabel) {
98324
+ return [
98325
+ { key: "Enter", label: "confirm" },
98326
+ { key: "esc", label: escLabel }
98327
+ ];
98328
+ }
98123
98329
  function parseSearchProviderArgs(line) {
98124
98330
  const parts = line.trim().length === 0 ? [] : line.trim().split(/\s+/);
98125
98331
  const [providerId, ...tail] = parts;
@@ -98166,27 +98372,28 @@ async function selectSearchProviderAndReport(controller, onSystem, providerId) {
98166
98372
  onSystem?.(`Search provider '${providerId}' selected.
98167
98373
  `);
98168
98374
  }
98169
- function promptSearchFieldStep(otui, r, field, value) {
98375
+ function promptSearchFieldStep(otui, target, field, value) {
98376
+ const r = stepRenderer(target);
98170
98377
  return new Promise((resolve) => {
98171
- const box = overlayBox(otui, r, "search-field-picker");
98172
- r.root.add(box);
98173
- box.add(new otui.TextRenderable(r, { id: "sf-title", content: otui.t`${otui.bold(field.label)} ${otui.dim(`${field.required ? "required" : "optional"} \xB7 Enter \xB7 Esc to go back`)}` }));
98378
+ const surface = openStepSurface(otui, target, {
98379
+ id: "search-field-picker",
98380
+ title: field.label,
98381
+ tab: field.required ? "Required" : "Optional",
98382
+ hint: `${field.required ? "required" : "optional"} \xB7 Enter \xB7 Esc to go back`,
98383
+ footer: inputStepFooter("back"),
98384
+ contentRows: 2,
98385
+ onEscape: () => {
98386
+ input.blur();
98387
+ resolve({ kind: "back" });
98388
+ }
98389
+ });
98174
98390
  const input = new otui.InputRenderable(r, { id: "sf-input", value, marginTop: 1 });
98175
- box.add(input);
98391
+ surface.body.add(input);
98176
98392
  input.focus();
98177
98393
  const cleanup = () => {
98178
- unsub();
98179
98394
  input.blur();
98180
- r.root.remove(box);
98395
+ surface.close();
98181
98396
  };
98182
- const unsub = onKeypress4(r, (key) => {
98183
- if (key.name === "escape") {
98184
- cleanup();
98185
- resolve({ kind: "back" });
98186
- key.preventDefault();
98187
- key.stopPropagation();
98188
- }
98189
- });
98190
98397
  input.on(otui.InputRenderableEvents.ENTER, () => {
98191
98398
  const entered = input.value.trim();
98192
98399
  cleanup();
@@ -98194,32 +98401,33 @@ function promptSearchFieldStep(otui, r, field, value) {
98194
98401
  });
98195
98402
  });
98196
98403
  }
98197
- function promptSearchCredentialStep(otui, r, opts) {
98404
+ function promptSearchCredentialStep(otui, target, opts) {
98405
+ const r = stepRenderer(target);
98198
98406
  return new Promise((resolve) => {
98199
- const box = overlayBox(otui, r, "search-credential-picker");
98200
- r.root.add(box);
98201
- box.add(new otui.TextRenderable(r, { id: "sc-title", content: otui.t`${otui.bold(`Paste your ${opts.label}`)} ${otui.dim("(Enter \xB7 Esc to go back)")}` }));
98202
- box.add(new otui.TextRenderable(r, {
98407
+ const surface = openStepSurface(otui, target, {
98408
+ id: "search-credential-picker",
98409
+ title: `Paste your ${opts.label}`,
98410
+ tab: "Credential",
98411
+ hint: "(Enter \xB7 Esc to go back)",
98412
+ footer: inputStepFooter("back"),
98413
+ contentRows: 4,
98414
+ onEscape: () => {
98415
+ keyInput.blur();
98416
+ resolve({ kind: "back" });
98417
+ }
98418
+ });
98419
+ surface.body.add(new otui.TextRenderable(r, {
98203
98420
  id: "sc-note",
98204
98421
  content: otui.t`${otui.dim("Saved to your keryx config dir (owner-only, 0600)")}`,
98205
98422
  marginTop: 1
98206
98423
  }));
98207
98424
  const keyInput = new otui.InputRenderable(r, { id: "sc-input", placeholder: "...", marginTop: 1 });
98208
- box.add(keyInput);
98425
+ surface.body.add(keyInput);
98209
98426
  keyInput.focus();
98210
98427
  const cleanup = () => {
98211
- unsub();
98212
98428
  keyInput.blur();
98213
- r.root.remove(box);
98429
+ surface.close();
98214
98430
  };
98215
- const unsub = onKeypress4(r, (key) => {
98216
- if (key.name === "escape") {
98217
- cleanup();
98218
- resolve({ kind: "back" });
98219
- key.preventDefault();
98220
- key.stopPropagation();
98221
- }
98222
- });
98223
98431
  keyInput.on(otui.InputRenderableEvents.ENTER, () => {
98224
98432
  const value = keyInput.value.trim();
98225
98433
  cleanup();
@@ -98227,14 +98435,24 @@ function promptSearchCredentialStep(otui, r, opts) {
98227
98435
  });
98228
98436
  });
98229
98437
  }
98230
- function promptSetActiveProviderStep(otui, r) {
98438
+ function promptSetActiveProviderStep(otui, target) {
98439
+ const r = stepRenderer(target);
98231
98440
  return new Promise((resolve) => {
98232
- const box = overlayBox(otui, r, "search-active-picker");
98233
- r.root.add(box);
98234
- box.add(new otui.TextRenderable(r, { id: "sa-title", content: otui.t`${otui.bold("Set as active provider after a successful test?")} ${otui.dim("(\u2191/\u2193, Enter \xB7 Esc to go back)")}` }));
98441
+ const surface = openStepSurface(otui, target, {
98442
+ id: "search-active-picker",
98443
+ title: "Set as active provider after a successful test?",
98444
+ tab: "Active",
98445
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to go back)",
98446
+ footer: selectStepFooter("back"),
98447
+ contentRows: selectBoxHeight(2, true),
98448
+ onEscape: () => {
98449
+ select.blur();
98450
+ resolve(undefined);
98451
+ }
98452
+ });
98235
98453
  const select = new otui.SelectRenderable(r, {
98236
98454
  id: "sa-select",
98237
- width: 60,
98455
+ width: surface.modal ? "100%" : 60,
98238
98456
  height: selectBoxHeight(2, true),
98239
98457
  options: [
98240
98458
  { name: "Yes", description: "select it once the test passes" },
@@ -98242,21 +98460,12 @@ function promptSetActiveProviderStep(otui, r) {
98242
98460
  ],
98243
98461
  selectedTextColor: "#ffd166"
98244
98462
  });
98245
- box.add(select);
98463
+ surface.body.add(select);
98246
98464
  select.focus();
98247
98465
  const cleanup = () => {
98248
- unsub();
98249
98466
  select.blur();
98250
- r.root.remove(box);
98467
+ surface.close();
98251
98468
  };
98252
- const unsub = onKeypress4(r, (key) => {
98253
- if (key.name === "escape") {
98254
- cleanup();
98255
- resolve(undefined);
98256
- key.preventDefault();
98257
- key.stopPropagation();
98258
- }
98259
- });
98260
98469
  select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98261
98470
  const chosen = select.getSelectedOption();
98262
98471
  cleanup();
@@ -98264,35 +98473,36 @@ function promptSetActiveProviderStep(otui, r) {
98264
98473
  });
98265
98474
  });
98266
98475
  }
98267
- function pickSearchProviderStep(otui, r, providers) {
98476
+ function pickSearchProviderStep(otui, target, providers) {
98477
+ const r = stepRenderer(target);
98268
98478
  return new Promise((resolve) => {
98269
- const box = overlayBox(otui, r, "search-provider-picker");
98270
- r.root.add(box);
98271
- box.add(new otui.TextRenderable(r, { id: "spp-title", content: otui.t`${otui.bold("Select a search provider")} ${otui.dim("(\u2191/\u2193, Enter \xB7 Esc to cancel)")}` }));
98479
+ const surface = openStepSurface(otui, target, {
98480
+ id: "search-provider-picker",
98481
+ title: "Select a search provider",
98482
+ tab: "Search providers",
98483
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to cancel)",
98484
+ footer: selectStepFooter("cancel"),
98485
+ contentRows: selectBoxHeight(providers.length, true),
98486
+ onEscape: () => {
98487
+ select.blur();
98488
+ resolve(undefined);
98489
+ }
98490
+ });
98272
98491
  const labelOf = (p) => `${p.id} (${p.displayName})`;
98273
98492
  const select = new otui.SelectRenderable(r, {
98274
98493
  id: "spp-select",
98275
- width: 60,
98494
+ width: surface.modal ? "100%" : 60,
98276
98495
  height: selectBoxHeight(providers.length, true),
98277
98496
  showScrollIndicator: true,
98278
98497
  options: providers.map((p) => ({ name: labelOf(p), description: p.kind })),
98279
98498
  selectedTextColor: "#ffd166"
98280
98499
  });
98281
- box.add(select);
98500
+ surface.body.add(select);
98282
98501
  select.focus();
98283
98502
  const cleanup = () => {
98284
- unsub();
98285
98503
  select.blur();
98286
- r.root.remove(box);
98504
+ surface.close();
98287
98505
  };
98288
- const unsub = onKeypress4(r, (key) => {
98289
- if (key.name === "escape") {
98290
- cleanup();
98291
- resolve(undefined);
98292
- key.preventDefault();
98293
- key.stopPropagation();
98294
- }
98295
- });
98296
98506
  select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98297
98507
  const chosen = select.getSelectedOption();
98298
98508
  cleanup();
@@ -98352,19 +98562,46 @@ async function runSearchProviderFieldsStep(otui, r, provider, seed) {
98352
98562
  }
98353
98563
  return { kind: "done", fields, credential, setActive };
98354
98564
  }
98355
- function runSearchProviderTestStep(otui, r, controller, provider, fields, credential, setActive) {
98565
+ function runSearchProviderTestStep(otui, target, controller, provider, fields, credential, setActive) {
98566
+ const r = stepRenderer(target);
98356
98567
  return new Promise((resolve) => {
98357
- const box = overlayBox(otui, r, "search-test-picker");
98358
- r.root.add(box);
98568
+ let settled;
98569
+ let left = false;
98570
+ const modal = isModalChrome(target);
98571
+ let surface;
98572
+ if (modal) {
98573
+ surface = openStepSurface(otui, target, {
98574
+ id: "search-test-picker",
98575
+ title: `Test '${provider.id}'`,
98576
+ tab: "Test",
98577
+ hint: "",
98578
+ footer: [
98579
+ { key: "Enter", label: "close after success" },
98580
+ { key: "esc", label: "back" }
98581
+ ],
98582
+ contentRows: 2,
98583
+ onEscape: () => {
98584
+ left = true;
98585
+ unsub();
98586
+ resolve(settled === "success" ? "done" : "retry");
98587
+ }
98588
+ });
98589
+ }
98590
+ const box = surface?.body ?? overlayBox(otui, r, "search-test-picker");
98591
+ if (surface === undefined)
98592
+ r.root.add(box);
98359
98593
  const status = new otui.TextRenderable(r, { id: "st-title", content: otui.t`${otui.bold(`Testing '${provider.id}'`)} ${otui.dim("...")}` });
98360
98594
  box.add(status);
98361
- let settled;
98362
98595
  const cleanup = () => {
98596
+ left = true;
98363
98597
  unsub();
98364
- r.root.remove(box);
98598
+ if (surface !== undefined)
98599
+ surface.close();
98600
+ else
98601
+ r.root.remove(box);
98365
98602
  };
98366
98603
  const unsub = onKeypress4(r, (key) => {
98367
- if (settled === "failure" && key.name === "escape") {
98604
+ if (!modal && settled === "failure" && key.name === "escape") {
98368
98605
  cleanup();
98369
98606
  resolve("retry");
98370
98607
  key.preventDefault();
@@ -98379,6 +98616,9 @@ function runSearchProviderTestStep(otui, r, controller, provider, fields, creden
98379
98616
  (async () => {
98380
98617
  controller.configure(provider.id, { ...provider.defaults, ...fields }, credential);
98381
98618
  const tested = await controller.test(provider.id);
98619
+ if (left) {
98620
+ return;
98621
+ }
98382
98622
  if (!tested.ok) {
98383
98623
  settled = "failure";
98384
98624
  const reason = tested.reason === "missing-credential" ? "missing credential" : "connection validation failed";
@@ -98391,6 +98631,9 @@ function runSearchProviderTestStep(otui, r, controller, provider, fields, creden
98391
98631
  return;
98392
98632
  }
98393
98633
  const selected = await controller.select(provider.id);
98634
+ if (left) {
98635
+ return;
98636
+ }
98394
98637
  status.content = selected.ok ? otui.t`${otui.green("\u2713")} ${otui.bold(`'${provider.id}' configured, tested, and set as active`)} ${otui.dim("(Enter to close)")}` : otui.t`${otui.green("\u2713")} ${otui.bold(`'${provider.id}' configured and tested`)} ${otui.dim(`but could not be set active (${selected.reason ?? "unknown"})`)} ${otui.dim("(Enter to close)")}`;
98395
98638
  })();
98396
98639
  });
@@ -98417,11 +98660,22 @@ async function searchProviderWizardInTui(otui, r, controller) {
98417
98660
  }
98418
98661
  }
98419
98662
  }
98420
- function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
98663
+ function promptBaseUrlStep(otui, target, label, baseUrl, notice) {
98664
+ const r = stepRenderer(target);
98421
98665
  return new Promise((resolve) => {
98422
- const box = overlayBox(otui, r, "base-url-picker");
98423
- r.root.add(box);
98424
- box.add(new otui.TextRenderable(r, { id: "bp-title", content: otui.t`${otui.bold(`${label} endpoint URL`)} ${otui.dim("(Enter \xB7 Esc to go back)")}` }));
98666
+ const surface = openStepSurface(otui, target, {
98667
+ id: "base-url-picker",
98668
+ title: `${label} endpoint URL`,
98669
+ tab: "Endpoint",
98670
+ hint: "(Enter \xB7 Esc to go back)",
98671
+ footer: inputStepFooter("back"),
98672
+ contentRows: notice !== undefined ? 6 : 4,
98673
+ onEscape: () => {
98674
+ input.blur();
98675
+ resolve(undefined);
98676
+ }
98677
+ });
98678
+ const box = surface.body;
98425
98679
  if (notice !== undefined) {
98426
98680
  box.add(new otui.TextRenderable(r, { id: "bp-notice", content: otui.t`${otui.red("\u2717")} ${notice}`, marginTop: 1 }));
98427
98681
  }
@@ -98430,17 +98684,9 @@ function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
98430
98684
  box.add(input);
98431
98685
  input.focus();
98432
98686
  const cleanup = () => {
98433
- unsub();
98434
- r.root.remove(box);
98687
+ input.blur();
98688
+ surface.close();
98435
98689
  };
98436
- const unsub = onKeypress4(r, (key) => {
98437
- if (key.name === "escape") {
98438
- cleanup();
98439
- resolve(undefined);
98440
- key.preventDefault();
98441
- key.stopPropagation();
98442
- }
98443
- });
98444
98690
  input.on(otui.InputRenderableEvents.ENTER, () => {
98445
98691
  const value = input.value.trim();
98446
98692
  cleanup();
@@ -98448,11 +98694,25 @@ function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
98448
98694
  });
98449
98695
  });
98450
98696
  }
98451
- function promptApiKeyStep(otui, r, opts) {
98697
+ function promptApiKeyStep(otui, target, opts) {
98698
+ const r = stepRenderer(target);
98452
98699
  return new Promise((resolve) => {
98453
- const box = overlayBox(otui, r, "key-picker");
98454
- r.root.add(box);
98455
- box.add(new otui.TextRenderable(r, { id: "kp-title", content: otui.t`${otui.bold(`Paste your ${opts.label} API key`)} ${otui.dim("(Enter \xB7 Esc to go back)")}` }));
98700
+ const surface = openStepSurface(otui, target, {
98701
+ id: "key-picker",
98702
+ title: `Paste your ${opts.label} API key`,
98703
+ tab: "API key",
98704
+ hint: "(Enter \xB7 Esc to go back)",
98705
+ footer: [
98706
+ { key: "Enter", label: "save (empty = skip)" },
98707
+ { key: "esc", label: "back" }
98708
+ ],
98709
+ contentRows: opts.notice !== undefined ? 6 : 4,
98710
+ onEscape: () => {
98711
+ keyInput.blur();
98712
+ resolve({ kind: "back" });
98713
+ }
98714
+ });
98715
+ const box = surface.body;
98456
98716
  if (opts.notice !== undefined) {
98457
98717
  box.add(new otui.TextRenderable(r, { id: "kp-notice", content: otui.t`${otui.red("\u2717")} ${opts.notice}`, marginTop: 1 }));
98458
98718
  }
@@ -98464,18 +98724,9 @@ function promptApiKeyStep(otui, r, opts) {
98464
98724
  const keyInput = new otui.InputRenderable(r, { id: "kp-input", placeholder: opts.placeholder ?? "sk-...", marginTop: 1 });
98465
98725
  box.add(keyInput);
98466
98726
  keyInput.focus();
98467
- const onKey = (key) => {
98468
- if (key.name === "escape") {
98469
- cleanup();
98470
- resolve({ kind: "back" });
98471
- key.preventDefault();
98472
- key.stopPropagation();
98473
- }
98474
- };
98475
- const unsub = onKeypress4(r, onKey);
98476
98727
  const cleanup = () => {
98477
- unsub();
98478
- r.root.remove(box);
98728
+ keyInput.blur();
98729
+ surface.close();
98479
98730
  };
98480
98731
  keyInput.on(otui.InputRenderableEvents.ENTER, () => {
98481
98732
  const value = keyInput.value.trim();
@@ -98484,38 +98735,39 @@ function promptApiKeyStep(otui, r, opts) {
98484
98735
  });
98485
98736
  });
98486
98737
  }
98487
- function pickAuthMethodStep(otui, r, providerLabel, methods) {
98738
+ function pickAuthMethodStep(otui, target, providerLabel, methods) {
98739
+ const r = stepRenderer(target);
98488
98740
  return new Promise((resolve) => {
98489
- const box = overlayBox(otui, r, "auth-method-picker");
98490
- r.root.add(box);
98491
- box.add(new otui.TextRenderable(r, { id: "amp-title", content: otui.t`${otui.bold(`How to connect ${providerLabel}`)} ${otui.dim("(\u2191/\u2193, Enter \xB7 Esc to go back)")}` }));
98741
+ const surface = openStepSurface(otui, target, {
98742
+ id: "auth-method-picker",
98743
+ title: `How to connect ${providerLabel}`,
98744
+ tab: "Sign-in",
98745
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to go back)",
98746
+ footer: selectStepFooter("back"),
98747
+ contentRows: selectBoxHeight(methods.length, true),
98748
+ onEscape: () => {
98749
+ select.blur();
98750
+ resolve(undefined);
98751
+ }
98752
+ });
98492
98753
  const descriptions = {
98493
98754
  "device-code": deviceCodeMethodLabel(providerLabel === "GitHub Copilot" ? "github-copilot" : providerLabel === "OpenAI" ? "openai" : "grok"),
98494
98755
  "api-key": "Manually enter API Key"
98495
98756
  };
98496
98757
  const select = new otui.SelectRenderable(r, {
98497
98758
  id: "amp-select",
98498
- width: 60,
98759
+ width: surface.modal ? "100%" : 60,
98499
98760
  height: selectBoxHeight(methods.length, true),
98500
98761
  showScrollIndicator: true,
98501
98762
  options: methods.map((m) => ({ name: m, description: descriptions[m] })),
98502
98763
  selectedTextColor: "#ffd166"
98503
98764
  });
98504
- box.add(select);
98765
+ surface.body.add(select);
98505
98766
  select.focus();
98506
98767
  const cleanup = () => {
98507
- unsub();
98508
98768
  select.blur();
98509
- r.root.remove(box);
98769
+ surface.close();
98510
98770
  };
98511
- const unsub = onKeypress4(r, (key) => {
98512
- if (key.name === "escape") {
98513
- cleanup();
98514
- resolve(undefined);
98515
- key.preventDefault();
98516
- key.stopPropagation();
98517
- }
98518
- });
98519
98771
  select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98520
98772
  const chosen = select.getSelectedOption();
98521
98773
  cleanup();
@@ -98523,30 +98775,27 @@ function pickAuthMethodStep(otui, r, providerLabel, methods) {
98523
98775
  });
98524
98776
  });
98525
98777
  }
98526
- function runDeviceLoginInTui(otui, r, provider, dir) {
98778
+ function runDeviceLoginInTui(otui, target, provider, dir) {
98779
+ const r = stepRenderer(target);
98527
98780
  return new Promise((resolve) => {
98528
- const box = overlayBox(otui, r, "device-login");
98529
- r.root.add(box);
98530
- box.add(new otui.TextRenderable(r, {
98531
- id: "dl-title",
98532
- content: otui.t`${otui.bold(deviceCodeMethodLabel(provider))} ${otui.dim("(Esc to cancel)")}`
98533
- }));
98534
- const status = new otui.TextRenderable(r, { id: "dl-status", content: otui.t`${otui.dim("Requesting a device code\u2026")}`, marginTop: 1 });
98535
- box.add(status);
98536
98781
  const controller = new AbortController;
98537
- const cleanup = () => {
98538
- unsub();
98539
- r.root.remove(box);
98540
- };
98541
- const unsub = onKeypress4(r, (key) => {
98542
- if (key.name === "escape") {
98782
+ const surface = openStepSurface(otui, target, {
98783
+ id: "device-login",
98784
+ title: deviceCodeMethodLabel(provider),
98785
+ tab: "Device login",
98786
+ hint: "(Esc to cancel)",
98787
+ footer: [{ key: "esc", label: "cancel" }],
98788
+ contentRows: 4,
98789
+ onEscape: () => {
98543
98790
  controller.abort();
98544
- cleanup();
98545
98791
  resolve(false);
98546
- key.preventDefault();
98547
- key.stopPropagation();
98548
98792
  }
98549
98793
  });
98794
+ const status = new otui.TextRenderable(r, { id: "dl-status", content: otui.t`${otui.dim("Requesting a device code\u2026")}`, marginTop: 1 });
98795
+ surface.body.add(status);
98796
+ const cleanup = () => {
98797
+ surface.close();
98798
+ };
98550
98799
  loginDeviceCode({
98551
98800
  provider,
98552
98801
  fetch: (input, init) => globalThis.fetch(input, init),
@@ -98588,15 +98837,26 @@ function modelPickerNotice(label, result) {
98588
98837
  }
98589
98838
  return modelsFailureLine(label, result.failure);
98590
98839
  }
98591
- function pickProviderStep(otui, r, detected) {
98840
+ function pickProviderStep(otui, target, detected) {
98841
+ const r = stepRenderer(target);
98592
98842
  return new Promise((resolve) => {
98593
- const box = overlayBox(otui, r, "picker");
98594
- r.root.add(box);
98595
- box.add(new otui.TextRenderable(r, { id: "picker-title", content: otui.t`${otui.bold("Select a provider")} ${otui.dim("(\u2191/\u2193, Enter \xB7 Esc to cancel)")}` }));
98843
+ const surface = openStepSurface(otui, target, {
98844
+ id: "picker",
98845
+ title: "Select a provider",
98846
+ tab: "Providers",
98847
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to cancel)",
98848
+ footer: selectStepFooter("cancel"),
98849
+ contentRows: selectBoxHeight(detected.length, true),
98850
+ onEscape: () => {
98851
+ provSelect.blur();
98852
+ resolve(undefined);
98853
+ }
98854
+ });
98855
+ const box = surface.body;
98596
98856
  const labelOf = (d) => d.label ?? d.name;
98597
98857
  const provSelect = new otui.SelectRenderable(r, {
98598
98858
  id: "picker-provider",
98599
- width: 60,
98859
+ width: surface.modal ? "100%" : 60,
98600
98860
  height: selectBoxHeight(detected.length, true),
98601
98861
  showScrollIndicator: true,
98602
98862
  options: detected.map((d) => ({ name: labelOf(d), description: d.note ?? `${d.models.length} model(s)` })),
@@ -98604,18 +98864,9 @@ function pickProviderStep(otui, r, detected) {
98604
98864
  });
98605
98865
  box.add(provSelect);
98606
98866
  provSelect.focus();
98607
- const onKey = (key) => {
98608
- if (key.name === "escape") {
98609
- cleanup();
98610
- resolve(undefined);
98611
- key.preventDefault();
98612
- key.stopPropagation();
98613
- }
98614
- };
98615
- const unsub = onKeypress4(r, onKey);
98616
98867
  const cleanup = () => {
98617
- unsub();
98618
- r.root.remove(box);
98868
+ provSelect.blur();
98869
+ surface.close();
98619
98870
  };
98620
98871
  provSelect.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98621
98872
  const chosen = provSelect.getSelectedOption();
@@ -98624,12 +98875,7 @@ function pickProviderStep(otui, r, detected) {
98624
98875
  });
98625
98876
  });
98626
98877
  }
98627
- function isModalChrome(target) {
98628
- return target !== null && typeof target === "object" && "renderer" in target && "focusComposer" in target;
98629
- }
98630
98878
  function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98631
- const chrome = isModalChrome(rOrChrome) ? rOrChrome : undefined;
98632
- const r = chrome !== undefined ? chrome.renderer : rOrChrome;
98633
98879
  return new Promise((resolve) => {
98634
98880
  if (detected.length === 0) {
98635
98881
  resolve(undefined);
@@ -98647,13 +98893,13 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98647
98893
  }
98648
98894
  const allCandidates = options.onlyConnected ? candidates : [...candidates, CUSTOM_ADD_ENTRY];
98649
98895
  while (true) {
98650
- const prov = await pickProviderStep(otui, r, allCandidates);
98896
+ const prov = await pickProviderStep(otui, rOrChrome, allCandidates);
98651
98897
  if (prov === undefined) {
98652
98898
  resolve(undefined);
98653
98899
  return;
98654
98900
  }
98655
98901
  if (prov.name === CUSTOM_PROVIDER_ADD_ID) {
98656
- const created = await promptCustomProviderWizard(otui, r);
98902
+ const created = await promptCustomProviderWizard(otui, rOrChrome);
98657
98903
  if (created !== undefined) {
98658
98904
  saveCustomCompatProvider({
98659
98905
  name: created.name,
@@ -98677,7 +98923,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98677
98923
  continue;
98678
98924
  }
98679
98925
  const lockDiscoveredHost = prov.name === "github-copilot";
98680
- let selectedBaseUrl = options.onlyConnected || prov.baseUrl === undefined || lockDiscoveredHost ? prov.baseUrl : await promptBaseUrlStep(otui, r, prov.label ?? prov.name, prov.baseUrl);
98926
+ let selectedBaseUrl = options.onlyConnected || prov.baseUrl === undefined || lockDiscoveredHost ? prov.baseUrl : await promptBaseUrlStep(otui, rOrChrome, prov.label ?? prov.name, prov.baseUrl);
98681
98927
  if (!options.onlyConnected && !lockDiscoveredHost && prov.baseUrl !== undefined && selectedBaseUrl === undefined) {
98682
98928
  continue;
98683
98929
  }
@@ -98705,13 +98951,13 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98705
98951
  offered.push("api-key");
98706
98952
  let method = offered.length === 1 ? offered[0] : undefined;
98707
98953
  if (offered.length > 1) {
98708
- method = await pickAuthMethodStep(otui, r, label, offered);
98954
+ method = await pickAuthMethodStep(otui, rOrChrome, label, offered);
98709
98955
  if (method === undefined) {
98710
98956
  return "back";
98711
98957
  }
98712
98958
  }
98713
98959
  if (method === "device-code") {
98714
- if (!await runDeviceLoginInTui(otui, r, prov.name, options.configDir)) {
98960
+ if (!await runDeviceLoginInTui(otui, rOrChrome, prov.name, options.configDir)) {
98715
98961
  return "back";
98716
98962
  }
98717
98963
  const saved = loadShellConfig(options.configDir).baseUrls?.[prov.name];
@@ -98721,7 +98967,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98721
98967
  }
98722
98968
  return "ok";
98723
98969
  }
98724
- const kr = await promptApiKeyStep(otui, r, {
98970
+ const kr = await promptApiKeyStep(otui, rOrChrome, {
98725
98971
  label,
98726
98972
  envKey,
98727
98973
  ...notice === undefined ? {} : { notice }
@@ -98751,7 +98997,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98751
98997
  models = await modelsForPicker(selectedProvider, modelDeps);
98752
98998
  }
98753
98999
  if (!options.onlyConnected && selectedProvider.baseUrl !== undefined && endpointMayBeAtFault(models.failure)) {
98754
- const corrected = await promptBaseUrlStep(otui, r, label, selectedProvider.baseUrl, modelsFailureLine(label, models.failure));
99000
+ const corrected = await promptBaseUrlStep(otui, rOrChrome, label, selectedProvider.baseUrl, modelsFailureLine(label, models.failure));
98755
99001
  if (corrected === undefined) {
98756
99002
  continue;
98757
99003
  }
@@ -98762,7 +99008,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98762
99008
  models = await modelsForPicker(selectedProvider, modelDeps);
98763
99009
  }
98764
99010
  }
98765
- const model = await pickModelInTui(otui, chrome ?? r, models.models, modelPickerNotice(label, models), {
99011
+ const model = await pickModelInTui(otui, rOrChrome, models.models, modelPickerNotice(label, models), {
98766
99012
  escLabel: "back"
98767
99013
  });
98768
99014
  if (model === undefined) {
@@ -98998,6 +99244,9 @@ function pickSessionInTui(otui, rOrChrome, sessions) {
98998
99244
  });
98999
99245
  });
99000
99246
  }
99247
+ function describeModeRow(mode, readOnly) {
99248
+ return readOnly ? { mode, readOnly: "\xB7 read-only" } : { mode };
99249
+ }
99001
99250
  var MODE_PICKER_DESCRIPTIONS = {
99002
99251
  ask: "Every mutating action asks first (today's default)",
99003
99252
  trust: "Auto-approves safe actions; still asks for destructive/credential ones",
@@ -99109,6 +99358,8 @@ async function launchTuiAgentShell(opts) {
99109
99358
  sidebar.add(new otui.TextRenderable(r, { id: "sb-model-k", content: otui.t`${otui.dim("Model")}`, marginTop: 1 }));
99110
99359
  const sbModelV = new otui.TextRenderable(r, { id: "sb-model-v", content: otui.t`${otui.dim(`${sel.provider}/${sel.model}`)}` });
99111
99360
  sidebar.add(sbModelV);
99361
+ const sbModeV = new otui.TextRenderable(r, { id: "sb-mode-v", content: "" });
99362
+ sidebar.add(sbModeV);
99112
99363
  sidebar.add(new otui.TextRenderable(r, { id: "sb-usage-k", content: otui.t`${otui.dim("Usage")}`, marginTop: 1 }));
99113
99364
  const sbUsageV = new otui.TextRenderable(r, { id: "sb-usage-v", content: otui.t`${otui.dim("\u21910 \u21930")}` });
99114
99365
  sidebar.add(sbUsageV);
@@ -99725,6 +99976,11 @@ async function launchTuiAgentShell(opts) {
99725
99976
  io.permissionMode = () => permissionMode;
99726
99977
  let readOnly = false;
99727
99978
  io.readOnly = () => readOnly;
99979
+ const paintModeRow = () => {
99980
+ const row = describeModeRow(permissionMode, readOnly);
99981
+ sbModeV.content = row.readOnly === undefined ? otui.t`${otui.dim(`mode ${row.mode}`)}` : otui.t`${otui.dim(`mode ${row.mode}`)} ${otui.yellow(row.readOnly)}`;
99982
+ };
99983
+ paintModeRow();
99728
99984
  io.onAutoApproved = (tool, input, meta) => {
99729
99985
  const preview = tool === "shell_exec" ? parseShellExecCommand(input) : tool;
99730
99986
  const label = meta.destructive ? `\u25C7 auto-approved (${permissionMode}) [destructive]` : `\u25C7 auto-approved (${permissionMode})`;
@@ -99752,7 +100008,12 @@ async function launchTuiAgentShell(opts) {
99752
100008
  };
99753
100009
  deps = { ...deps, onContextCompaction };
99754
100010
  liveDeps = deps;
100011
+ let removeSplash;
99755
100012
  const applyOpened = (opened, previewHistory) => {
100013
+ if (opened.history.length > 0) {
100014
+ removeSplash?.();
100015
+ removeSplash = undefined;
100016
+ }
99756
100017
  liveSession = opened.handle;
99757
100018
  history = previewHistory === true ? opened.history.slice(-SESSION_PREVIEW_MESSAGE_COUNT) : opened.history;
99758
100019
  archive = opened.archive.length > 0 ? [...opened.archive] : [...opened.history];
@@ -99868,6 +100129,9 @@ async function launchTuiAgentShell(opts) {
99868
100129
  }));
99869
100130
  }
99870
100131
  slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
100132
+ if (history.length === 0) {
100133
+ removeSplash = mountEmptyTranscriptSplash(otui, r, transcript);
100134
+ }
99871
100135
  refreshWorkspaceSidebar();
99872
100136
  refreshReviewSidebar();
99873
100137
  const paintSessionHeader = () => {
@@ -100164,6 +100428,7 @@ Staying in the current session.
100164
100428
  }
100165
100429
  }
100166
100430
  permissionMode = next;
100431
+ paintModeRow();
100167
100432
  chrome.showToast(`Permission mode: ${next}`);
100168
100433
  if (saveFlag) {
100169
100434
  const saved = setProjectPermissionMode(sessionCwd, next);
@@ -100221,11 +100486,13 @@ Staying in the current session.
100221
100486
  }
100222
100487
  if (wanted === "on") {
100223
100488
  readOnly = true;
100489
+ paintModeRow();
100224
100490
  chrome.showToast("Read-only mode: on");
100225
100491
  return;
100226
100492
  }
100227
100493
  if (wanted === "off") {
100228
100494
  readOnly = false;
100495
+ paintModeRow();
100229
100496
  chrome.showToast(`Read-only mode: off (permission mode stays: ${permissionMode})`);
100230
100497
  return;
100231
100498
  }
@@ -100605,6 +100872,8 @@ Staying in the current session.
100605
100872
  suggestionGate.cancel("new turn started");
100606
100873
  if (origin === "operator") {
100607
100874
  consecutiveAutoWakes = 0;
100875
+ removeSplash?.();
100876
+ removeSplash = undefined;
100608
100877
  }
100609
100878
  const displayLine = summarizeSubmittedLine(line);
100610
100879
  if (chrome.isBusy()) {
@@ -100910,7 +101179,7 @@ Staying in the current session.
100910
101179
  const args = parseSearchProviderArgs(line.slice(16));
100911
101180
  const all = searchProviderController.configurable();
100912
101181
  if (args.providerId === undefined) {
100913
- await chrome.withOverlay(() => searchProviderWizardInTui(otui, r, searchProviderController));
101182
+ await chrome.withOverlay(() => searchProviderWizardInTui(otui, chrome, searchProviderController));
100914
101183
  input.focus();
100915
101184
  return;
100916
101185
  }
@@ -100946,7 +101215,7 @@ Staying in the current session.
100946
101215
  `);
100947
101216
  return;
100948
101217
  }
100949
- const picked = await chrome.withOverlay(() => pickSearchProviderStep(otui, r, selectable));
101218
+ const picked = await chrome.withOverlay(() => pickSearchProviderStep(otui, chrome, selectable));
100950
101219
  input.focus();
100951
101220
  if (picked === undefined) {
100952
101221
  return;
@@ -101136,12 +101405,10 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
101136
101405
  const ns = await chrome.withOverlay(() => command.name === "/connect" ? selectProviderModelInTui(otui, chrome, detected, { onlyConnected: true, env: process.env }) : selectProviderModelInTui(otui, chrome, detected));
101137
101406
  if (ns !== undefined) {
101138
101407
  await switchTo(ns);
101139
- } else {
101140
- if (command.name === "/connect") {
101141
- chrome.showToast("No connected providers found. Run /provider to configure one first.");
101142
- }
101143
- input.focus();
101408
+ } else if (command.name === "/connect") {
101409
+ chrome.showToast("No connected providers found. Run /provider to configure one first.");
101144
101410
  }
101411
+ input.focus();
101145
101412
  })();
101146
101413
  return;
101147
101414
  }
@@ -106956,7 +107223,7 @@ import { readFileSync as readFileSync9 } from "fs";
106956
107223
 
106957
107224
  // src/agents/bootstrap.ts
106958
107225
  import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
106959
- import { homedir as homedir9 } from "os";
107226
+ import { homedir as homedir10 } from "os";
106960
107227
  import path200 from "path";
106961
107228
  init_fs();
106962
107229
  var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
@@ -107025,7 +107292,7 @@ function resolveAgentBootstrapRuntimes(ids) {
107025
107292
  }
107026
107293
  return { runtimes, unknown };
107027
107294
  }
107028
- async function agentBootstrapStatus(runtime, homeRoot = homedir9()) {
107295
+ async function agentBootstrapStatus(runtime, homeRoot = homedir10()) {
107029
107296
  const filePath = runtime.filePath(homeRoot);
107030
107297
  const exists = await pathExists(filePath);
107031
107298
  const content = exists ? await readFile101(filePath, "utf8") : "";
@@ -107035,7 +107302,7 @@ async function agentBootstrapStatus(runtime, homeRoot = homedir9()) {
107035
107302
  return { runtime: runtime.id, label: runtime.label, filePath, exists, installed, current };
107036
107303
  }
107037
107304
  async function installAgentBootstrap(runtime, options = {}) {
107038
- const homeRoot = options.homeRoot ?? homedir9();
107305
+ const homeRoot = options.homeRoot ?? homedir10();
107039
107306
  const filePath = runtime.filePath(homeRoot);
107040
107307
  const exists = await pathExists(filePath);
107041
107308
  const current = exists ? await readFile101(filePath, "utf8") : "";
@@ -107050,7 +107317,7 @@ async function installAgentBootstrap(runtime, options = {}) {
107050
107317
  return { ...status, wrote, dryRun };
107051
107318
  }
107052
107319
  async function uninstallAgentBootstrap(runtime, options = {}) {
107053
- const homeRoot = options.homeRoot ?? homedir9();
107320
+ const homeRoot = options.homeRoot ?? homedir10();
107054
107321
  const filePath = runtime.filePath(homeRoot);
107055
107322
  const exists = await pathExists(filePath);
107056
107323
  const current = exists ? await readFile101(filePath, "utf8") : "";