@mrciphersmith/keryx 0.2.117 → 0.2.119

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 +517 -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.119",
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,90 @@ 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
+ if (end < lineCounts.length && used < rows) {
91441
+ return { start: from, end: end + 1, lastLines: rows - used };
91442
+ }
91443
+ return { start: from, end };
91337
91444
  }
91338
91445
  function isActionable(id) {
91339
91446
  return id !== "generic";
@@ -91348,7 +91455,7 @@ function formatOtherServers(otherServers) {
91348
91455
  const list = rest > 0 ? `${shown.join(", ")}, +${rest} more` : shown.join(", ");
91349
91456
  return ` \xB7 also has: ${list}`;
91350
91457
  }
91351
- function formatMcpRowLine(runtime, isSelected, status) {
91458
+ function formatMcpRowLine(runtime, isSelected, status, width) {
91352
91459
  const mark = isSelected ? ">" : " ";
91353
91460
  const label = runtimeLabel(runtime.id).padEnd(20);
91354
91461
  const statusText = runtime.connected ? "\u25CF keryx connected" : "\u25CB keryx not connected";
@@ -91364,7 +91471,8 @@ function formatMcpRowLine(runtime, isSelected, status) {
91364
91471
  } else {
91365
91472
  action = runtime.connected ? " [d] disconnect" : " [c] connect";
91366
91473
  }
91367
- return `${mark} ${label} ${statusText}${action}${formatOtherServers(runtime.otherServers)}`;
91474
+ return wrapHangingRow(`${mark} ${label} `, `${statusText}${action}${formatOtherServers(runtime.otherServers)}`, width).join(`
91475
+ `);
91368
91476
  }
91369
91477
  function asRowTarget(body) {
91370
91478
  const parent = body;
@@ -91388,6 +91496,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91388
91496
  let rowCtor;
91389
91497
  let activeRenderer;
91390
91498
  let unsubscribeKey;
91499
+ let bodyWidth;
91391
91500
  const rendererHint = options.renderer ?? chrome?.renderer;
91392
91501
  const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
91393
91502
  const paintToolsRows = () => {
@@ -91400,11 +91509,25 @@ function presentMcpTools(openModal, otui, chrome, options) {
91400
91509
  toolsBody.add(new rowCtor(activeRenderer, { id: "mcp-tools-empty", content: "No tools available." }));
91401
91510
  return;
91402
91511
  }
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) }));
91512
+ toolsBody.add(new rowCtor(activeRenderer, { id: "mcp-tools-columns", content: TOOLS_COLUMN_HEADER }));
91513
+ const rows = options.tools.map((tool) => formatToolRowLines(tool, bodyWidth));
91514
+ const window = toolsWindow(rows);
91515
+ toolsScroll = window.start;
91516
+ for (let index = window.start;index < window.end; index++) {
91517
+ const lines = rows[index] ?? [];
91518
+ const cut = index === window.end - 1 && window.lastLines !== undefined;
91519
+ toolsBody.add(new rowCtor(activeRenderer, {
91520
+ id: `mcp-tool-row-${index}`,
91521
+ content: (cut ? lines.slice(0, window.lastLines) : lines).join(`
91522
+ `)
91523
+ }));
91406
91524
  }
91407
91525
  };
91526
+ const toolsRowBudget = () => {
91527
+ const captionLines = wrapHangingRow("", TOOLS_TAB_HEADER, bodyWidth, 0).length;
91528
+ return Math.max(1, bodyRows - captionLines - 1);
91529
+ };
91530
+ const toolsWindow = (rows) => fitRowsByLines(rows.map((lines) => lines.length), toolsScroll, toolsRowBudget());
91408
91531
  const paintMcpRows = () => {
91409
91532
  if (mcpBody === undefined || rowCtor === undefined) {
91410
91533
  return;
@@ -91416,18 +91539,19 @@ function presentMcpTools(openModal, otui, chrome, options) {
91416
91539
  mcpBody.add(new rowCtor(activeRenderer, { id: "mcp-mcp-empty", content: "No MCP client runtimes registered." }));
91417
91540
  return;
91418
91541
  }
91542
+ mcpBody.add(new rowCtor(activeRenderer, { id: "mcp-mcp-keys", content: MCP_TAB_KEYS }));
91419
91543
  const start = clampScroll3(mcpScroll, runtimes.length, bodyRows);
91420
91544
  for (const [i, runtime] of runtimes.slice(start, start + bodyRows).entries()) {
91421
91545
  const index = start + i;
91422
91546
  mcpBody.add(new rowCtor(activeRenderer, {
91423
91547
  id: `mcp-row-${runtime.id}`,
91424
- content: formatMcpRowLine(runtime, index === mcpSelected, status),
91548
+ content: formatMcpRowLine(runtime, index === mcpSelected, status, bodyWidth),
91425
91549
  onMouseDown: () => handleRowClick(runtime.id, index)
91426
91550
  }));
91427
91551
  }
91428
91552
  };
91429
91553
  const paint = () => {
91430
- toolsScroll = clampScroll3(toolsScroll, options.tools.length, bodyRows);
91554
+ toolsScroll = Math.max(0, toolsScroll);
91431
91555
  mcpScroll = scrollToReveal3(mcpSelected, mcpScroll, bodyRows);
91432
91556
  mcpScroll = clampScroll3(mcpScroll, runtimes.length, bodyRows);
91433
91557
  paintToolsRows();
@@ -91510,9 +91634,10 @@ function presentMcpTools(openModal, otui, chrome, options) {
91510
91634
  }
91511
91635
  rowCtor = ctor;
91512
91636
  activeRenderer = renderer;
91637
+ bodyWidth = ctx?.width;
91513
91638
  if (tabId === "tools") {
91514
91639
  toolsBody = target;
91515
- toolsScroll = clampScroll3(toolsScroll, options.tools.length, bodyRows);
91640
+ toolsScroll = Math.max(0, toolsScroll);
91516
91641
  paintToolsRows();
91517
91642
  return;
91518
91643
  }
@@ -91560,7 +91685,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91560
91685
  if (onMcp) {
91561
91686
  moveMcpSelection(mcpSelected - 1);
91562
91687
  } else {
91563
- toolsScroll = clampScroll3(toolsScroll - 1, options.tools.length, bodyRows);
91688
+ toolsScroll = Math.max(0, toolsScroll - 1);
91564
91689
  paint();
91565
91690
  }
91566
91691
  return;
@@ -91569,7 +91694,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91569
91694
  if (onMcp) {
91570
91695
  moveMcpSelection(mcpSelected + 1);
91571
91696
  } else {
91572
- toolsScroll = clampScroll3(toolsScroll + 1, options.tools.length, bodyRows);
91697
+ toolsScroll += 1;
91573
91698
  paint();
91574
91699
  }
91575
91700
  return;
@@ -91579,7 +91704,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
91579
91704
  if (onMcp) {
91580
91705
  mcpScroll = clampScroll3(mcpScroll + step, runtimes.length, bodyRows);
91581
91706
  } else {
91582
- toolsScroll = clampScroll3(toolsScroll + step, options.tools.length, bodyRows);
91707
+ toolsScroll = Math.max(0, toolsScroll + step);
91583
91708
  }
91584
91709
  paint();
91585
91710
  }
@@ -91592,6 +91717,7 @@ function openMcpTools(otui, chrome, options) {
91592
91717
  }
91593
91718
 
91594
91719
  // src/tui/mcp-consumer.ts
91720
+ import { homedir as homedir9 } from "os";
91595
91721
  var MCP_CONSUMER_COMMAND = "/mcp";
91596
91722
  function isMcpConsumerCommand(line) {
91597
91723
  return (line.trim().split(/\s+/)[0] ?? "") === MCP_CONSUMER_COMMAND;
@@ -91620,12 +91746,21 @@ function credentialNames(server) {
91620
91746
  }
91621
91747
  return [...names].sort();
91622
91748
  }
91623
- function targetOf(server) {
91749
+ var MAX_TARGET_CHARS = 240;
91750
+ function collapseHome2(text, home) {
91751
+ const trimmed = home.replace(/\/+$/, "");
91752
+ if (trimmed === "")
91753
+ return text;
91754
+ const escaped = trimmed.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
91755
+ return text.replace(new RegExp(`(^|[\\s="'])${escaped}(?=/|$|[\\s"'])`, "g"), "$1~");
91756
+ }
91757
+ function targetOf(server, home) {
91624
91758
  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);
91759
+ const target = typeof raw.url === "string" && raw.url.length > 0 ? displayUrl(raw.url) : collapseHome2([raw.command, ...raw.args ?? []].filter(Boolean).join(" "), home);
91760
+ return sanitiseIdentifier(target, MAX_TARGET_CHARS);
91627
91761
  }
91628
91762
  function buildConsumerModel(input) {
91763
+ const home = input.home ?? homedir9();
91629
91764
  const stateByName = new Map(input.states.map((s) => [s.name, s]));
91630
91765
  const rows = input.configured.map((server) => {
91631
91766
  const state = stateByName.get(server.name);
@@ -91637,7 +91772,7 @@ function buildConsumerModel(input) {
91637
91772
  transport: transportOf(server),
91638
91773
  status,
91639
91774
  toolCount: state?.toolCount ?? 0,
91640
- target: targetOf(server),
91775
+ target: targetOf(server, home),
91641
91776
  credentials: credentialNames(server),
91642
91777
  detail,
91643
91778
  action: status === "needs-approval" ? `keryx mcp trust ${server.name}` : status === "needs_auth" ? "set the variable named above, then restart the session" : undefined
@@ -91645,10 +91780,10 @@ function buildConsumerModel(input) {
91645
91780
  });
91646
91781
  return {
91647
91782
  rows,
91648
- problems: input.problems.map((p) => `${p.file}: ${sanitiseForDisplay(p.message)}`),
91783
+ problems: input.problems.map((p) => `${collapseHome2(p.file, home)}: ${sanitiseForDisplay(p.message)}`),
91649
91784
  emptyHint: rows.length > 0 ? undefined : `No MCP servers configured.
91650
- user: ${input.userFile}
91651
- project: ${input.projectFile}`
91785
+ user: ${collapseHome2(input.userFile, home)}
91786
+ project: ${collapseHome2(input.projectFile, home)}`
91652
91787
  };
91653
91788
  }
91654
91789
  var MCP_CONSUMER_FOOTER = [
@@ -91677,7 +91812,7 @@ function consumerActionFor(status) {
91677
91812
  function statusGlyph(status) {
91678
91813
  return STATUS_GLYPH[status] ?? status;
91679
91814
  }
91680
- function formatConsumerModalRow(row, isSelected, status) {
91815
+ function formatConsumerModalRow(row, isSelected, status, width) {
91681
91816
  const mark = isSelected ? ">" : " ";
91682
91817
  const name = row.name.padEnd(18);
91683
91818
  const source = row.source.padEnd(10);
@@ -91702,7 +91837,8 @@ function formatConsumerModalRow(row, isSelected, status) {
91702
91837
  action = "";
91703
91838
  }
91704
91839
  const creds = row.credentials.length > 0 ? ` [reads ${row.credentials.join(", ")}]` : "";
91705
- return `${mark} ${name} ${source} ${transport} ${glyph}${tools}${action} ${row.target}${creds}`.trimEnd();
91840
+ return wrapHangingRow(`${mark} ${name} ${source} ${transport} ${glyph}`, `${tools}${action} ${row.target}${creds}`, width).join(`
91841
+ `);
91706
91842
  }
91707
91843
  function formatConsumerModalLines(model, selected, status) {
91708
91844
  if (model === undefined) {
@@ -91743,6 +91879,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
91743
91879
  let rowCtor;
91744
91880
  let activeRenderer;
91745
91881
  let unsubscribeKey;
91882
+ let bodyWidth;
91746
91883
  const rendererHint = options.renderer ?? chrome?.renderer;
91747
91884
  const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
91748
91885
  const rowsOf = () => options.snapshot()?.rows ?? [];
@@ -91775,7 +91912,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
91775
91912
  const index = start + i;
91776
91913
  body.add(new rowCtor(activeRenderer, {
91777
91914
  id: `mcp-consumer-row-${row.name}`,
91778
- content: formatConsumerModalRow(row, index === selected, status),
91915
+ content: formatConsumerModalRow(row, index === selected, status, bodyWidth),
91779
91916
  onMouseDown: () => handleRowClick(row.name, index)
91780
91917
  }));
91781
91918
  }
@@ -91846,6 +91983,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
91846
91983
  rowCtor = ctor;
91847
91984
  activeRenderer = renderer;
91848
91985
  body = target;
91986
+ bodyWidth = ctx?.width;
91849
91987
  paint();
91850
91988
  },
91851
91989
  onClose: () => {
@@ -97654,30 +97792,31 @@ var CUSTOM_ADD_ENTRY = {
97654
97792
  label: "\uFF0B Add custom provider (OpenAI-compatible)",
97655
97793
  note: "name \xB7 URL \xB7 key \xB7 models \u2192 saved to llm-providers.json"
97656
97794
  };
97657
- function promptCustomFieldStep(otui, r, opts) {
97795
+ function promptCustomFieldStep(otui, target, opts) {
97796
+ const r = stepRenderer(target);
97658
97797
  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)")}` }));
97798
+ const surface = openStepSurface(otui, target, {
97799
+ id: "custom-field-picker",
97800
+ title: opts.title,
97801
+ tab: "Custom provider",
97802
+ hint: "(Enter \xB7 Esc to go back)",
97803
+ footer: inputStepFooter("back"),
97804
+ contentRows: opts.note !== undefined ? 4 : 2,
97805
+ onEscape: () => {
97806
+ input.blur();
97807
+ resolve({ kind: "back" });
97808
+ }
97809
+ });
97662
97810
  if (opts.note !== undefined) {
97663
- box.add(new otui.TextRenderable(r, { id: "cf-note", content: otui.t`${otui.dim(opts.note)}`, marginTop: 1 }));
97811
+ surface.body.add(new otui.TextRenderable(r, { id: "cf-note", content: otui.t`${otui.dim(opts.note)}`, marginTop: 1 }));
97664
97812
  }
97665
97813
  const input = new otui.InputRenderable(r, { id: "cf-input", value: opts.value ?? "", marginTop: 1 });
97666
- box.add(input);
97814
+ surface.body.add(input);
97667
97815
  input.focus();
97668
97816
  const cleanup = () => {
97669
- unsub();
97670
97817
  input.blur();
97671
- r.root.remove(box);
97818
+ surface.close();
97672
97819
  };
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
97820
  input.on(otui.InputRenderableEvents.ENTER, () => {
97682
97821
  const entered = input.value.trim();
97683
97822
  cleanup();
@@ -97697,9 +97836,9 @@ function parseOptionalPositiveWizardNumber(raw) {
97697
97836
  const parsed = parseOptionalWizardNumber(raw);
97698
97837
  return parsed !== undefined && parsed > 0 ? parsed : undefined;
97699
97838
  }
97700
- async function promptCustomProviderWizard(otui, r) {
97839
+ async function promptCustomProviderWizard(otui, target) {
97701
97840
  while (true) {
97702
- const nameStep = await promptCustomFieldStep(otui, r, {
97841
+ const nameStep = await promptCustomFieldStep(otui, target, {
97703
97842
  title: "Provider name",
97704
97843
  note: "unique id used in /provider (letters, digits, - _)"
97705
97844
  });
@@ -97711,7 +97850,7 @@ async function promptCustomProviderWizard(otui, r) {
97711
97850
  continue;
97712
97851
  }
97713
97852
  while (true) {
97714
- const urlStep = await promptCustomFieldStep(otui, r, {
97853
+ const urlStep = await promptCustomFieldStep(otui, target, {
97715
97854
  title: "Base URL",
97716
97855
  note: "before the chat path \u2014 /v1/chat/completions is appended, e.g. http://10.110.43.19:8080",
97717
97856
  value: "http://localhost:8000"
@@ -97733,7 +97872,7 @@ async function promptCustomProviderWizard(otui, r) {
97733
97872
  continue;
97734
97873
  }
97735
97874
  const baseUrl = baseUrlRaw.replace(/\/+$/, "");
97736
- const keyStep = await promptCustomFieldStep(otui, r, {
97875
+ const keyStep = await promptCustomFieldStep(otui, target, {
97737
97876
  title: "API key (optional)",
97738
97877
  note: "empty = no key \xB7 saved owner-only (0600) in your keryx config dir"
97739
97878
  });
@@ -97741,7 +97880,7 @@ async function promptCustomProviderWizard(otui, r) {
97741
97880
  continue;
97742
97881
  }
97743
97882
  const apiKey = keyStep.value.length > 0 ? keyStep.value : undefined;
97744
- const modelsStep = await promptCustomFieldStep(otui, r, {
97883
+ const modelsStep = await promptCustomFieldStep(otui, target, {
97745
97884
  title: "Models (optional)",
97746
97885
  note: "comma-separated ids \xB7 empty = fetch the live /v1/models list"
97747
97886
  });
@@ -97749,7 +97888,7 @@ async function promptCustomProviderWizard(otui, r) {
97749
97888
  continue;
97750
97889
  }
97751
97890
  const models = modelsStep.value.split(",").map((m) => m.trim()).filter((m) => m.length > 0);
97752
- const temperatureStep = await promptCustomFieldStep(otui, r, {
97891
+ const temperatureStep = await promptCustomFieldStep(otui, target, {
97753
97892
  title: "Temperature (optional)",
97754
97893
  note: "empty = provider default \xB7 e.g. 0.2"
97755
97894
  });
@@ -97757,7 +97896,7 @@ async function promptCustomProviderWizard(otui, r) {
97757
97896
  continue;
97758
97897
  }
97759
97898
  const temperature = parseOptionalWizardNumber(temperatureStep.value);
97760
- const maxOutputTokensStep = await promptCustomFieldStep(otui, r, {
97899
+ const maxOutputTokensStep = await promptCustomFieldStep(otui, target, {
97761
97900
  title: "Max output tokens (optional)",
97762
97901
  note: "empty = default 1024 \xB7 e.g. 4096"
97763
97902
  });
@@ -97765,7 +97904,7 @@ async function promptCustomProviderWizard(otui, r) {
97765
97904
  continue;
97766
97905
  }
97767
97906
  const maxOutputTokens = parseOptionalPositiveWizardNumber(maxOutputTokensStep.value);
97768
- const timeoutMsStep = await promptCustomFieldStep(otui, r, {
97907
+ const timeoutMsStep = await promptCustomFieldStep(otui, target, {
97769
97908
  title: "Request timeout ms (optional)",
97770
97909
  note: "empty = no engine-internal timeout \xB7 e.g. 180000"
97771
97910
  });
@@ -98120,6 +98259,78 @@ function onKeypress4(r, handler) {
98120
98259
  r._internalKeyInput.onInternal("keypress", handler);
98121
98260
  return () => r._internalKeyInput.offInternal("keypress", handler);
98122
98261
  }
98262
+ function isModalChrome(target) {
98263
+ return target !== null && typeof target === "object" && "renderer" in target && "focusComposer" in target;
98264
+ }
98265
+ function stepRenderer(target) {
98266
+ return isModalChrome(target) ? target.renderer : target;
98267
+ }
98268
+ function openStepSurface(otui, target, opts) {
98269
+ const r = stepRenderer(target);
98270
+ if (!isModalChrome(target)) {
98271
+ const box = overlayBox(otui, r, opts.id);
98272
+ r.root.add(box);
98273
+ box.add(new otui.TextRenderable(r, { id: `${opts.id}-title`, content: otui.t`${otui.bold(opts.title)} ${otui.dim(opts.hint)}` }));
98274
+ let closed = false;
98275
+ const close = () => {
98276
+ if (closed)
98277
+ return;
98278
+ closed = true;
98279
+ unsub();
98280
+ r.root.remove(box);
98281
+ };
98282
+ const unsub = onKeypress4(r, (key) => {
98283
+ if (key.name === "escape") {
98284
+ close();
98285
+ opts.onEscape();
98286
+ key.preventDefault();
98287
+ key.stopPropagation();
98288
+ }
98289
+ });
98290
+ return { body: box, modal: false, close };
98291
+ }
98292
+ let body;
98293
+ let closedByStep = false;
98294
+ const handle = openModal(otui, target, {
98295
+ title: opts.title,
98296
+ tabs: [{ id: opts.id, label: opts.tab }],
98297
+ footer: opts.footer,
98298
+ contentRows: opts.contentRows,
98299
+ renderTab: (_tabId, tabBody) => {
98300
+ body = tabBody;
98301
+ },
98302
+ onClose: () => {
98303
+ if (!closedByStep)
98304
+ opts.onEscape();
98305
+ }
98306
+ });
98307
+ if (handle === undefined || body === undefined) {
98308
+ return openStepSurface(otui, r, opts);
98309
+ }
98310
+ return {
98311
+ body,
98312
+ modal: true,
98313
+ close: () => {
98314
+ if (closedByStep)
98315
+ return;
98316
+ closedByStep = true;
98317
+ handle.close({ restoreFocus: false });
98318
+ }
98319
+ };
98320
+ }
98321
+ function selectStepFooter(escLabel) {
98322
+ return [
98323
+ { key: "\u2191/\u2193", label: "select" },
98324
+ { key: "Enter", label: "confirm" },
98325
+ { key: "esc", label: escLabel }
98326
+ ];
98327
+ }
98328
+ function inputStepFooter(escLabel) {
98329
+ return [
98330
+ { key: "Enter", label: "confirm" },
98331
+ { key: "esc", label: escLabel }
98332
+ ];
98333
+ }
98123
98334
  function parseSearchProviderArgs(line) {
98124
98335
  const parts = line.trim().length === 0 ? [] : line.trim().split(/\s+/);
98125
98336
  const [providerId, ...tail] = parts;
@@ -98166,27 +98377,28 @@ async function selectSearchProviderAndReport(controller, onSystem, providerId) {
98166
98377
  onSystem?.(`Search provider '${providerId}' selected.
98167
98378
  `);
98168
98379
  }
98169
- function promptSearchFieldStep(otui, r, field, value) {
98380
+ function promptSearchFieldStep(otui, target, field, value) {
98381
+ const r = stepRenderer(target);
98170
98382
  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`)}` }));
98383
+ const surface = openStepSurface(otui, target, {
98384
+ id: "search-field-picker",
98385
+ title: field.label,
98386
+ tab: field.required ? "Required" : "Optional",
98387
+ hint: `${field.required ? "required" : "optional"} \xB7 Enter \xB7 Esc to go back`,
98388
+ footer: inputStepFooter("back"),
98389
+ contentRows: 2,
98390
+ onEscape: () => {
98391
+ input.blur();
98392
+ resolve({ kind: "back" });
98393
+ }
98394
+ });
98174
98395
  const input = new otui.InputRenderable(r, { id: "sf-input", value, marginTop: 1 });
98175
- box.add(input);
98396
+ surface.body.add(input);
98176
98397
  input.focus();
98177
98398
  const cleanup = () => {
98178
- unsub();
98179
98399
  input.blur();
98180
- r.root.remove(box);
98400
+ surface.close();
98181
98401
  };
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
98402
  input.on(otui.InputRenderableEvents.ENTER, () => {
98191
98403
  const entered = input.value.trim();
98192
98404
  cleanup();
@@ -98194,32 +98406,33 @@ function promptSearchFieldStep(otui, r, field, value) {
98194
98406
  });
98195
98407
  });
98196
98408
  }
98197
- function promptSearchCredentialStep(otui, r, opts) {
98409
+ function promptSearchCredentialStep(otui, target, opts) {
98410
+ const r = stepRenderer(target);
98198
98411
  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, {
98412
+ const surface = openStepSurface(otui, target, {
98413
+ id: "search-credential-picker",
98414
+ title: `Paste your ${opts.label}`,
98415
+ tab: "Credential",
98416
+ hint: "(Enter \xB7 Esc to go back)",
98417
+ footer: inputStepFooter("back"),
98418
+ contentRows: 4,
98419
+ onEscape: () => {
98420
+ keyInput.blur();
98421
+ resolve({ kind: "back" });
98422
+ }
98423
+ });
98424
+ surface.body.add(new otui.TextRenderable(r, {
98203
98425
  id: "sc-note",
98204
98426
  content: otui.t`${otui.dim("Saved to your keryx config dir (owner-only, 0600)")}`,
98205
98427
  marginTop: 1
98206
98428
  }));
98207
98429
  const keyInput = new otui.InputRenderable(r, { id: "sc-input", placeholder: "...", marginTop: 1 });
98208
- box.add(keyInput);
98430
+ surface.body.add(keyInput);
98209
98431
  keyInput.focus();
98210
98432
  const cleanup = () => {
98211
- unsub();
98212
98433
  keyInput.blur();
98213
- r.root.remove(box);
98434
+ surface.close();
98214
98435
  };
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
98436
  keyInput.on(otui.InputRenderableEvents.ENTER, () => {
98224
98437
  const value = keyInput.value.trim();
98225
98438
  cleanup();
@@ -98227,14 +98440,24 @@ function promptSearchCredentialStep(otui, r, opts) {
98227
98440
  });
98228
98441
  });
98229
98442
  }
98230
- function promptSetActiveProviderStep(otui, r) {
98443
+ function promptSetActiveProviderStep(otui, target) {
98444
+ const r = stepRenderer(target);
98231
98445
  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)")}` }));
98446
+ const surface = openStepSurface(otui, target, {
98447
+ id: "search-active-picker",
98448
+ title: "Set as active provider after a successful test?",
98449
+ tab: "Active",
98450
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to go back)",
98451
+ footer: selectStepFooter("back"),
98452
+ contentRows: selectBoxHeight(2, true),
98453
+ onEscape: () => {
98454
+ select.blur();
98455
+ resolve(undefined);
98456
+ }
98457
+ });
98235
98458
  const select = new otui.SelectRenderable(r, {
98236
98459
  id: "sa-select",
98237
- width: 60,
98460
+ width: surface.modal ? "100%" : 60,
98238
98461
  height: selectBoxHeight(2, true),
98239
98462
  options: [
98240
98463
  { name: "Yes", description: "select it once the test passes" },
@@ -98242,21 +98465,12 @@ function promptSetActiveProviderStep(otui, r) {
98242
98465
  ],
98243
98466
  selectedTextColor: "#ffd166"
98244
98467
  });
98245
- box.add(select);
98468
+ surface.body.add(select);
98246
98469
  select.focus();
98247
98470
  const cleanup = () => {
98248
- unsub();
98249
98471
  select.blur();
98250
- r.root.remove(box);
98472
+ surface.close();
98251
98473
  };
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
98474
  select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98261
98475
  const chosen = select.getSelectedOption();
98262
98476
  cleanup();
@@ -98264,35 +98478,36 @@ function promptSetActiveProviderStep(otui, r) {
98264
98478
  });
98265
98479
  });
98266
98480
  }
98267
- function pickSearchProviderStep(otui, r, providers) {
98481
+ function pickSearchProviderStep(otui, target, providers) {
98482
+ const r = stepRenderer(target);
98268
98483
  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)")}` }));
98484
+ const surface = openStepSurface(otui, target, {
98485
+ id: "search-provider-picker",
98486
+ title: "Select a search provider",
98487
+ tab: "Search providers",
98488
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to cancel)",
98489
+ footer: selectStepFooter("cancel"),
98490
+ contentRows: selectBoxHeight(providers.length, true),
98491
+ onEscape: () => {
98492
+ select.blur();
98493
+ resolve(undefined);
98494
+ }
98495
+ });
98272
98496
  const labelOf = (p) => `${p.id} (${p.displayName})`;
98273
98497
  const select = new otui.SelectRenderable(r, {
98274
98498
  id: "spp-select",
98275
- width: 60,
98499
+ width: surface.modal ? "100%" : 60,
98276
98500
  height: selectBoxHeight(providers.length, true),
98277
98501
  showScrollIndicator: true,
98278
98502
  options: providers.map((p) => ({ name: labelOf(p), description: p.kind })),
98279
98503
  selectedTextColor: "#ffd166"
98280
98504
  });
98281
- box.add(select);
98505
+ surface.body.add(select);
98282
98506
  select.focus();
98283
98507
  const cleanup = () => {
98284
- unsub();
98285
98508
  select.blur();
98286
- r.root.remove(box);
98509
+ surface.close();
98287
98510
  };
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
98511
  select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98297
98512
  const chosen = select.getSelectedOption();
98298
98513
  cleanup();
@@ -98352,19 +98567,46 @@ async function runSearchProviderFieldsStep(otui, r, provider, seed) {
98352
98567
  }
98353
98568
  return { kind: "done", fields, credential, setActive };
98354
98569
  }
98355
- function runSearchProviderTestStep(otui, r, controller, provider, fields, credential, setActive) {
98570
+ function runSearchProviderTestStep(otui, target, controller, provider, fields, credential, setActive) {
98571
+ const r = stepRenderer(target);
98356
98572
  return new Promise((resolve) => {
98357
- const box = overlayBox(otui, r, "search-test-picker");
98358
- r.root.add(box);
98573
+ let settled;
98574
+ let left = false;
98575
+ const modal = isModalChrome(target);
98576
+ let surface;
98577
+ if (modal) {
98578
+ surface = openStepSurface(otui, target, {
98579
+ id: "search-test-picker",
98580
+ title: `Test '${provider.id}'`,
98581
+ tab: "Test",
98582
+ hint: "",
98583
+ footer: [
98584
+ { key: "Enter", label: "close after success" },
98585
+ { key: "esc", label: "back" }
98586
+ ],
98587
+ contentRows: 2,
98588
+ onEscape: () => {
98589
+ left = true;
98590
+ unsub();
98591
+ resolve(settled === "success" ? "done" : "retry");
98592
+ }
98593
+ });
98594
+ }
98595
+ const box = surface?.body ?? overlayBox(otui, r, "search-test-picker");
98596
+ if (surface === undefined)
98597
+ r.root.add(box);
98359
98598
  const status = new otui.TextRenderable(r, { id: "st-title", content: otui.t`${otui.bold(`Testing '${provider.id}'`)} ${otui.dim("...")}` });
98360
98599
  box.add(status);
98361
- let settled;
98362
98600
  const cleanup = () => {
98601
+ left = true;
98363
98602
  unsub();
98364
- r.root.remove(box);
98603
+ if (surface !== undefined)
98604
+ surface.close();
98605
+ else
98606
+ r.root.remove(box);
98365
98607
  };
98366
98608
  const unsub = onKeypress4(r, (key) => {
98367
- if (settled === "failure" && key.name === "escape") {
98609
+ if (!modal && settled === "failure" && key.name === "escape") {
98368
98610
  cleanup();
98369
98611
  resolve("retry");
98370
98612
  key.preventDefault();
@@ -98379,6 +98621,9 @@ function runSearchProviderTestStep(otui, r, controller, provider, fields, creden
98379
98621
  (async () => {
98380
98622
  controller.configure(provider.id, { ...provider.defaults, ...fields }, credential);
98381
98623
  const tested = await controller.test(provider.id);
98624
+ if (left) {
98625
+ return;
98626
+ }
98382
98627
  if (!tested.ok) {
98383
98628
  settled = "failure";
98384
98629
  const reason = tested.reason === "missing-credential" ? "missing credential" : "connection validation failed";
@@ -98391,6 +98636,9 @@ function runSearchProviderTestStep(otui, r, controller, provider, fields, creden
98391
98636
  return;
98392
98637
  }
98393
98638
  const selected = await controller.select(provider.id);
98639
+ if (left) {
98640
+ return;
98641
+ }
98394
98642
  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
98643
  })();
98396
98644
  });
@@ -98417,11 +98665,22 @@ async function searchProviderWizardInTui(otui, r, controller) {
98417
98665
  }
98418
98666
  }
98419
98667
  }
98420
- function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
98668
+ function promptBaseUrlStep(otui, target, label, baseUrl, notice) {
98669
+ const r = stepRenderer(target);
98421
98670
  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)")}` }));
98671
+ const surface = openStepSurface(otui, target, {
98672
+ id: "base-url-picker",
98673
+ title: `${label} endpoint URL`,
98674
+ tab: "Endpoint",
98675
+ hint: "(Enter \xB7 Esc to go back)",
98676
+ footer: inputStepFooter("back"),
98677
+ contentRows: notice !== undefined ? 6 : 4,
98678
+ onEscape: () => {
98679
+ input.blur();
98680
+ resolve(undefined);
98681
+ }
98682
+ });
98683
+ const box = surface.body;
98425
98684
  if (notice !== undefined) {
98426
98685
  box.add(new otui.TextRenderable(r, { id: "bp-notice", content: otui.t`${otui.red("\u2717")} ${notice}`, marginTop: 1 }));
98427
98686
  }
@@ -98430,17 +98689,9 @@ function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
98430
98689
  box.add(input);
98431
98690
  input.focus();
98432
98691
  const cleanup = () => {
98433
- unsub();
98434
- r.root.remove(box);
98692
+ input.blur();
98693
+ surface.close();
98435
98694
  };
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
98695
  input.on(otui.InputRenderableEvents.ENTER, () => {
98445
98696
  const value = input.value.trim();
98446
98697
  cleanup();
@@ -98448,11 +98699,25 @@ function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
98448
98699
  });
98449
98700
  });
98450
98701
  }
98451
- function promptApiKeyStep(otui, r, opts) {
98702
+ function promptApiKeyStep(otui, target, opts) {
98703
+ const r = stepRenderer(target);
98452
98704
  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)")}` }));
98705
+ const surface = openStepSurface(otui, target, {
98706
+ id: "key-picker",
98707
+ title: `Paste your ${opts.label} API key`,
98708
+ tab: "API key",
98709
+ hint: "(Enter \xB7 Esc to go back)",
98710
+ footer: [
98711
+ { key: "Enter", label: "save (empty = skip)" },
98712
+ { key: "esc", label: "back" }
98713
+ ],
98714
+ contentRows: opts.notice !== undefined ? 6 : 4,
98715
+ onEscape: () => {
98716
+ keyInput.blur();
98717
+ resolve({ kind: "back" });
98718
+ }
98719
+ });
98720
+ const box = surface.body;
98456
98721
  if (opts.notice !== undefined) {
98457
98722
  box.add(new otui.TextRenderable(r, { id: "kp-notice", content: otui.t`${otui.red("\u2717")} ${opts.notice}`, marginTop: 1 }));
98458
98723
  }
@@ -98464,18 +98729,9 @@ function promptApiKeyStep(otui, r, opts) {
98464
98729
  const keyInput = new otui.InputRenderable(r, { id: "kp-input", placeholder: opts.placeholder ?? "sk-...", marginTop: 1 });
98465
98730
  box.add(keyInput);
98466
98731
  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
98732
  const cleanup = () => {
98477
- unsub();
98478
- r.root.remove(box);
98733
+ keyInput.blur();
98734
+ surface.close();
98479
98735
  };
98480
98736
  keyInput.on(otui.InputRenderableEvents.ENTER, () => {
98481
98737
  const value = keyInput.value.trim();
@@ -98484,38 +98740,39 @@ function promptApiKeyStep(otui, r, opts) {
98484
98740
  });
98485
98741
  });
98486
98742
  }
98487
- function pickAuthMethodStep(otui, r, providerLabel, methods) {
98743
+ function pickAuthMethodStep(otui, target, providerLabel, methods) {
98744
+ const r = stepRenderer(target);
98488
98745
  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)")}` }));
98746
+ const surface = openStepSurface(otui, target, {
98747
+ id: "auth-method-picker",
98748
+ title: `How to connect ${providerLabel}`,
98749
+ tab: "Sign-in",
98750
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to go back)",
98751
+ footer: selectStepFooter("back"),
98752
+ contentRows: selectBoxHeight(methods.length, true),
98753
+ onEscape: () => {
98754
+ select.blur();
98755
+ resolve(undefined);
98756
+ }
98757
+ });
98492
98758
  const descriptions = {
98493
98759
  "device-code": deviceCodeMethodLabel(providerLabel === "GitHub Copilot" ? "github-copilot" : providerLabel === "OpenAI" ? "openai" : "grok"),
98494
98760
  "api-key": "Manually enter API Key"
98495
98761
  };
98496
98762
  const select = new otui.SelectRenderable(r, {
98497
98763
  id: "amp-select",
98498
- width: 60,
98764
+ width: surface.modal ? "100%" : 60,
98499
98765
  height: selectBoxHeight(methods.length, true),
98500
98766
  showScrollIndicator: true,
98501
98767
  options: methods.map((m) => ({ name: m, description: descriptions[m] })),
98502
98768
  selectedTextColor: "#ffd166"
98503
98769
  });
98504
- box.add(select);
98770
+ surface.body.add(select);
98505
98771
  select.focus();
98506
98772
  const cleanup = () => {
98507
- unsub();
98508
98773
  select.blur();
98509
- r.root.remove(box);
98774
+ surface.close();
98510
98775
  };
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
98776
  select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98520
98777
  const chosen = select.getSelectedOption();
98521
98778
  cleanup();
@@ -98523,30 +98780,27 @@ function pickAuthMethodStep(otui, r, providerLabel, methods) {
98523
98780
  });
98524
98781
  });
98525
98782
  }
98526
- function runDeviceLoginInTui(otui, r, provider, dir) {
98783
+ function runDeviceLoginInTui(otui, target, provider, dir) {
98784
+ const r = stepRenderer(target);
98527
98785
  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
98786
  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") {
98787
+ const surface = openStepSurface(otui, target, {
98788
+ id: "device-login",
98789
+ title: deviceCodeMethodLabel(provider),
98790
+ tab: "Device login",
98791
+ hint: "(Esc to cancel)",
98792
+ footer: [{ key: "esc", label: "cancel" }],
98793
+ contentRows: 4,
98794
+ onEscape: () => {
98543
98795
  controller.abort();
98544
- cleanup();
98545
98796
  resolve(false);
98546
- key.preventDefault();
98547
- key.stopPropagation();
98548
98797
  }
98549
98798
  });
98799
+ const status = new otui.TextRenderable(r, { id: "dl-status", content: otui.t`${otui.dim("Requesting a device code\u2026")}`, marginTop: 1 });
98800
+ surface.body.add(status);
98801
+ const cleanup = () => {
98802
+ surface.close();
98803
+ };
98550
98804
  loginDeviceCode({
98551
98805
  provider,
98552
98806
  fetch: (input, init) => globalThis.fetch(input, init),
@@ -98588,15 +98842,26 @@ function modelPickerNotice(label, result) {
98588
98842
  }
98589
98843
  return modelsFailureLine(label, result.failure);
98590
98844
  }
98591
- function pickProviderStep(otui, r, detected) {
98845
+ function pickProviderStep(otui, target, detected) {
98846
+ const r = stepRenderer(target);
98592
98847
  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)")}` }));
98848
+ const surface = openStepSurface(otui, target, {
98849
+ id: "picker",
98850
+ title: "Select a provider",
98851
+ tab: "Providers",
98852
+ hint: "(\u2191/\u2193, Enter \xB7 Esc to cancel)",
98853
+ footer: selectStepFooter("cancel"),
98854
+ contentRows: selectBoxHeight(detected.length, true),
98855
+ onEscape: () => {
98856
+ provSelect.blur();
98857
+ resolve(undefined);
98858
+ }
98859
+ });
98860
+ const box = surface.body;
98596
98861
  const labelOf = (d) => d.label ?? d.name;
98597
98862
  const provSelect = new otui.SelectRenderable(r, {
98598
98863
  id: "picker-provider",
98599
- width: 60,
98864
+ width: surface.modal ? "100%" : 60,
98600
98865
  height: selectBoxHeight(detected.length, true),
98601
98866
  showScrollIndicator: true,
98602
98867
  options: detected.map((d) => ({ name: labelOf(d), description: d.note ?? `${d.models.length} model(s)` })),
@@ -98604,18 +98869,9 @@ function pickProviderStep(otui, r, detected) {
98604
98869
  });
98605
98870
  box.add(provSelect);
98606
98871
  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
98872
  const cleanup = () => {
98617
- unsub();
98618
- r.root.remove(box);
98873
+ provSelect.blur();
98874
+ surface.close();
98619
98875
  };
98620
98876
  provSelect.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
98621
98877
  const chosen = provSelect.getSelectedOption();
@@ -98624,12 +98880,7 @@ function pickProviderStep(otui, r, detected) {
98624
98880
  });
98625
98881
  });
98626
98882
  }
98627
- function isModalChrome(target) {
98628
- return target !== null && typeof target === "object" && "renderer" in target && "focusComposer" in target;
98629
- }
98630
98883
  function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98631
- const chrome = isModalChrome(rOrChrome) ? rOrChrome : undefined;
98632
- const r = chrome !== undefined ? chrome.renderer : rOrChrome;
98633
98884
  return new Promise((resolve) => {
98634
98885
  if (detected.length === 0) {
98635
98886
  resolve(undefined);
@@ -98647,13 +98898,13 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98647
98898
  }
98648
98899
  const allCandidates = options.onlyConnected ? candidates : [...candidates, CUSTOM_ADD_ENTRY];
98649
98900
  while (true) {
98650
- const prov = await pickProviderStep(otui, r, allCandidates);
98901
+ const prov = await pickProviderStep(otui, rOrChrome, allCandidates);
98651
98902
  if (prov === undefined) {
98652
98903
  resolve(undefined);
98653
98904
  return;
98654
98905
  }
98655
98906
  if (prov.name === CUSTOM_PROVIDER_ADD_ID) {
98656
- const created = await promptCustomProviderWizard(otui, r);
98907
+ const created = await promptCustomProviderWizard(otui, rOrChrome);
98657
98908
  if (created !== undefined) {
98658
98909
  saveCustomCompatProvider({
98659
98910
  name: created.name,
@@ -98677,7 +98928,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98677
98928
  continue;
98678
98929
  }
98679
98930
  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);
98931
+ let selectedBaseUrl = options.onlyConnected || prov.baseUrl === undefined || lockDiscoveredHost ? prov.baseUrl : await promptBaseUrlStep(otui, rOrChrome, prov.label ?? prov.name, prov.baseUrl);
98681
98932
  if (!options.onlyConnected && !lockDiscoveredHost && prov.baseUrl !== undefined && selectedBaseUrl === undefined) {
98682
98933
  continue;
98683
98934
  }
@@ -98705,13 +98956,13 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98705
98956
  offered.push("api-key");
98706
98957
  let method = offered.length === 1 ? offered[0] : undefined;
98707
98958
  if (offered.length > 1) {
98708
- method = await pickAuthMethodStep(otui, r, label, offered);
98959
+ method = await pickAuthMethodStep(otui, rOrChrome, label, offered);
98709
98960
  if (method === undefined) {
98710
98961
  return "back";
98711
98962
  }
98712
98963
  }
98713
98964
  if (method === "device-code") {
98714
- if (!await runDeviceLoginInTui(otui, r, prov.name, options.configDir)) {
98965
+ if (!await runDeviceLoginInTui(otui, rOrChrome, prov.name, options.configDir)) {
98715
98966
  return "back";
98716
98967
  }
98717
98968
  const saved = loadShellConfig(options.configDir).baseUrls?.[prov.name];
@@ -98721,7 +98972,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98721
98972
  }
98722
98973
  return "ok";
98723
98974
  }
98724
- const kr = await promptApiKeyStep(otui, r, {
98975
+ const kr = await promptApiKeyStep(otui, rOrChrome, {
98725
98976
  label,
98726
98977
  envKey,
98727
98978
  ...notice === undefined ? {} : { notice }
@@ -98751,7 +99002,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98751
99002
  models = await modelsForPicker(selectedProvider, modelDeps);
98752
99003
  }
98753
99004
  if (!options.onlyConnected && selectedProvider.baseUrl !== undefined && endpointMayBeAtFault(models.failure)) {
98754
- const corrected = await promptBaseUrlStep(otui, r, label, selectedProvider.baseUrl, modelsFailureLine(label, models.failure));
99005
+ const corrected = await promptBaseUrlStep(otui, rOrChrome, label, selectedProvider.baseUrl, modelsFailureLine(label, models.failure));
98755
99006
  if (corrected === undefined) {
98756
99007
  continue;
98757
99008
  }
@@ -98762,7 +99013,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
98762
99013
  models = await modelsForPicker(selectedProvider, modelDeps);
98763
99014
  }
98764
99015
  }
98765
- const model = await pickModelInTui(otui, chrome ?? r, models.models, modelPickerNotice(label, models), {
99016
+ const model = await pickModelInTui(otui, rOrChrome, models.models, modelPickerNotice(label, models), {
98766
99017
  escLabel: "back"
98767
99018
  });
98768
99019
  if (model === undefined) {
@@ -98998,6 +99249,9 @@ function pickSessionInTui(otui, rOrChrome, sessions) {
98998
99249
  });
98999
99250
  });
99000
99251
  }
99252
+ function describeModeRow(mode, readOnly) {
99253
+ return readOnly ? { mode, readOnly: "\xB7 read-only" } : { mode };
99254
+ }
99001
99255
  var MODE_PICKER_DESCRIPTIONS = {
99002
99256
  ask: "Every mutating action asks first (today's default)",
99003
99257
  trust: "Auto-approves safe actions; still asks for destructive/credential ones",
@@ -99109,6 +99363,8 @@ async function launchTuiAgentShell(opts) {
99109
99363
  sidebar.add(new otui.TextRenderable(r, { id: "sb-model-k", content: otui.t`${otui.dim("Model")}`, marginTop: 1 }));
99110
99364
  const sbModelV = new otui.TextRenderable(r, { id: "sb-model-v", content: otui.t`${otui.dim(`${sel.provider}/${sel.model}`)}` });
99111
99365
  sidebar.add(sbModelV);
99366
+ const sbModeV = new otui.TextRenderable(r, { id: "sb-mode-v", content: "" });
99367
+ sidebar.add(sbModeV);
99112
99368
  sidebar.add(new otui.TextRenderable(r, { id: "sb-usage-k", content: otui.t`${otui.dim("Usage")}`, marginTop: 1 }));
99113
99369
  const sbUsageV = new otui.TextRenderable(r, { id: "sb-usage-v", content: otui.t`${otui.dim("\u21910 \u21930")}` });
99114
99370
  sidebar.add(sbUsageV);
@@ -99725,6 +99981,11 @@ async function launchTuiAgentShell(opts) {
99725
99981
  io.permissionMode = () => permissionMode;
99726
99982
  let readOnly = false;
99727
99983
  io.readOnly = () => readOnly;
99984
+ const paintModeRow = () => {
99985
+ const row = describeModeRow(permissionMode, readOnly);
99986
+ sbModeV.content = row.readOnly === undefined ? otui.t`${otui.dim(`mode ${row.mode}`)}` : otui.t`${otui.dim(`mode ${row.mode}`)} ${otui.yellow(row.readOnly)}`;
99987
+ };
99988
+ paintModeRow();
99728
99989
  io.onAutoApproved = (tool, input, meta) => {
99729
99990
  const preview = tool === "shell_exec" ? parseShellExecCommand(input) : tool;
99730
99991
  const label = meta.destructive ? `\u25C7 auto-approved (${permissionMode}) [destructive]` : `\u25C7 auto-approved (${permissionMode})`;
@@ -99752,7 +100013,12 @@ async function launchTuiAgentShell(opts) {
99752
100013
  };
99753
100014
  deps = { ...deps, onContextCompaction };
99754
100015
  liveDeps = deps;
100016
+ let removeSplash;
99755
100017
  const applyOpened = (opened, previewHistory) => {
100018
+ if (opened.history.length > 0) {
100019
+ removeSplash?.();
100020
+ removeSplash = undefined;
100021
+ }
99756
100022
  liveSession = opened.handle;
99757
100023
  history = previewHistory === true ? opened.history.slice(-SESSION_PREVIEW_MESSAGE_COUNT) : opened.history;
99758
100024
  archive = opened.archive.length > 0 ? [...opened.archive] : [...opened.history];
@@ -99868,6 +100134,9 @@ async function launchTuiAgentShell(opts) {
99868
100134
  }));
99869
100135
  }
99870
100136
  slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
100137
+ if (history.length === 0) {
100138
+ removeSplash = mountEmptyTranscriptSplash(otui, r, transcript);
100139
+ }
99871
100140
  refreshWorkspaceSidebar();
99872
100141
  refreshReviewSidebar();
99873
100142
  const paintSessionHeader = () => {
@@ -100164,6 +100433,7 @@ Staying in the current session.
100164
100433
  }
100165
100434
  }
100166
100435
  permissionMode = next;
100436
+ paintModeRow();
100167
100437
  chrome.showToast(`Permission mode: ${next}`);
100168
100438
  if (saveFlag) {
100169
100439
  const saved = setProjectPermissionMode(sessionCwd, next);
@@ -100221,11 +100491,13 @@ Staying in the current session.
100221
100491
  }
100222
100492
  if (wanted === "on") {
100223
100493
  readOnly = true;
100494
+ paintModeRow();
100224
100495
  chrome.showToast("Read-only mode: on");
100225
100496
  return;
100226
100497
  }
100227
100498
  if (wanted === "off") {
100228
100499
  readOnly = false;
100500
+ paintModeRow();
100229
100501
  chrome.showToast(`Read-only mode: off (permission mode stays: ${permissionMode})`);
100230
100502
  return;
100231
100503
  }
@@ -100605,6 +100877,8 @@ Staying in the current session.
100605
100877
  suggestionGate.cancel("new turn started");
100606
100878
  if (origin === "operator") {
100607
100879
  consecutiveAutoWakes = 0;
100880
+ removeSplash?.();
100881
+ removeSplash = undefined;
100608
100882
  }
100609
100883
  const displayLine = summarizeSubmittedLine(line);
100610
100884
  if (chrome.isBusy()) {
@@ -100910,7 +101184,7 @@ Staying in the current session.
100910
101184
  const args = parseSearchProviderArgs(line.slice(16));
100911
101185
  const all = searchProviderController.configurable();
100912
101186
  if (args.providerId === undefined) {
100913
- await chrome.withOverlay(() => searchProviderWizardInTui(otui, r, searchProviderController));
101187
+ await chrome.withOverlay(() => searchProviderWizardInTui(otui, chrome, searchProviderController));
100914
101188
  input.focus();
100915
101189
  return;
100916
101190
  }
@@ -100946,7 +101220,7 @@ Staying in the current session.
100946
101220
  `);
100947
101221
  return;
100948
101222
  }
100949
- const picked = await chrome.withOverlay(() => pickSearchProviderStep(otui, r, selectable));
101223
+ const picked = await chrome.withOverlay(() => pickSearchProviderStep(otui, chrome, selectable));
100950
101224
  input.focus();
100951
101225
  if (picked === undefined) {
100952
101226
  return;
@@ -101136,12 +101410,10 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
101136
101410
  const ns = await chrome.withOverlay(() => command.name === "/connect" ? selectProviderModelInTui(otui, chrome, detected, { onlyConnected: true, env: process.env }) : selectProviderModelInTui(otui, chrome, detected));
101137
101411
  if (ns !== undefined) {
101138
101412
  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();
101413
+ } else if (command.name === "/connect") {
101414
+ chrome.showToast("No connected providers found. Run /provider to configure one first.");
101144
101415
  }
101416
+ input.focus();
101145
101417
  })();
101146
101418
  return;
101147
101419
  }
@@ -106956,7 +107228,7 @@ import { readFileSync as readFileSync9 } from "fs";
106956
107228
 
106957
107229
  // src/agents/bootstrap.ts
106958
107230
  import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
106959
- import { homedir as homedir9 } from "os";
107231
+ import { homedir as homedir10 } from "os";
106960
107232
  import path200 from "path";
106961
107233
  init_fs();
106962
107234
  var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
@@ -107025,7 +107297,7 @@ function resolveAgentBootstrapRuntimes(ids) {
107025
107297
  }
107026
107298
  return { runtimes, unknown };
107027
107299
  }
107028
- async function agentBootstrapStatus(runtime, homeRoot = homedir9()) {
107300
+ async function agentBootstrapStatus(runtime, homeRoot = homedir10()) {
107029
107301
  const filePath = runtime.filePath(homeRoot);
107030
107302
  const exists = await pathExists(filePath);
107031
107303
  const content = exists ? await readFile101(filePath, "utf8") : "";
@@ -107035,7 +107307,7 @@ async function agentBootstrapStatus(runtime, homeRoot = homedir9()) {
107035
107307
  return { runtime: runtime.id, label: runtime.label, filePath, exists, installed, current };
107036
107308
  }
107037
107309
  async function installAgentBootstrap(runtime, options = {}) {
107038
- const homeRoot = options.homeRoot ?? homedir9();
107310
+ const homeRoot = options.homeRoot ?? homedir10();
107039
107311
  const filePath = runtime.filePath(homeRoot);
107040
107312
  const exists = await pathExists(filePath);
107041
107313
  const current = exists ? await readFile101(filePath, "utf8") : "";
@@ -107050,7 +107322,7 @@ async function installAgentBootstrap(runtime, options = {}) {
107050
107322
  return { ...status, wrote, dryRun };
107051
107323
  }
107052
107324
  async function uninstallAgentBootstrap(runtime, options = {}) {
107053
- const homeRoot = options.homeRoot ?? homedir9();
107325
+ const homeRoot = options.homeRoot ?? homedir10();
107054
107326
  const filePath = runtime.filePath(homeRoot);
107055
107327
  const exists = await pathExists(filePath);
107056
107328
  const current = exists ? await readFile101(filePath, "utf8") : "";