u-foo 2.3.22 → 2.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "2.3.22",
3
+ "version": "2.3.23",
4
4
  "description": "Multi-Agent Workspace Protocol. Just add u. claude → uclaude, codex → ucodex.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://ufoo.dev",
@@ -61,6 +61,7 @@ function createAgentViewController(options = {}) {
61
61
  let busLogLines = [];
62
62
  let busStartupAgentId = "";
63
63
  let busStartupLineCount = 0;
64
+ let busAgentReplyActive = false;
64
65
  const originalRender = screen.render.bind(screen);
65
66
  let renderFrozen = false;
66
67
 
@@ -361,6 +362,7 @@ function createAgentViewController(options = {}) {
361
362
  function resetBusView(agentId) {
362
363
  busInputValue = "";
363
364
  busInputCursor = 0;
365
+ busAgentReplyActive = false;
364
366
  busStartupAgentId = agentId || "";
365
367
  const label = getAgentLabel(agentId);
366
368
  const startupLines = staticStartupLines(agentId, label, getCols());
@@ -390,6 +392,42 @@ function createAgentViewController(options = {}) {
390
392
  if (busLogLines.length > 1000) {
391
393
  busLogLines = busLogLines.slice(-1000);
392
394
  }
395
+ if (clean.endsWith("\n")) {
396
+ busAgentReplyActive = false;
397
+ }
398
+ }
399
+
400
+ function ensureBusLinePrefix(prefix = "") {
401
+ if (busLogLines.length === 0) {
402
+ busLogLines.push(prefix);
403
+ return;
404
+ }
405
+ if (busLogLines[busLogLines.length - 1] === "") {
406
+ busLogLines[busLogLines.length - 1] = prefix;
407
+ return;
408
+ }
409
+ busLogLines.push(prefix);
410
+ }
411
+
412
+ function appendBusAgentReply(text = "") {
413
+ const clean = stripAnsi(String(text || "")).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
414
+ if (!clean) return;
415
+ for (const char of clean) {
416
+ if (char === "\n") {
417
+ busLogLines.push("");
418
+ continue;
419
+ }
420
+ if (!busAgentReplyActive) {
421
+ ensureBusLinePrefix("• ");
422
+ busAgentReplyActive = true;
423
+ } else if (busLogLines.length === 0 || busLogLines[busLogLines.length - 1] === "") {
424
+ ensureBusLinePrefix(" ");
425
+ }
426
+ busLogLines[busLogLines.length - 1] += char;
427
+ }
428
+ if (busLogLines.length > 1000) {
429
+ busLogLines = busLogLines.slice(-1000);
430
+ }
393
431
  }
394
432
 
395
433
  function getBusInputViewport(width) {
@@ -533,6 +571,7 @@ function createAgentViewController(options = {}) {
533
571
  busLogLines = [];
534
572
  busStartupAgentId = "";
535
573
  busStartupLineCount = 0;
574
+ busAgentReplyActive = false;
536
575
 
537
576
  currentView = "main";
538
577
  viewingAgent = null;
@@ -661,6 +700,7 @@ function createAgentViewController(options = {}) {
661
700
  return;
662
701
  }
663
702
  appendBusLog(`> ${text}\n`);
703
+ busAgentReplyActive = false;
664
704
  busInputValue = "";
665
705
  busInputCursor = 0;
666
706
  sendBusMessage(viewingAgent, text);
@@ -767,7 +807,7 @@ function createAgentViewController(options = {}) {
767
807
  .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
768
808
  .replace(/\x1b\[(?:[?>=]?[0-9]*c|[?]?6n|5n)/g, "");
769
809
  if (agentViewUsesBus) {
770
- appendBusLog(cleaned);
810
+ appendBusAgentReply(cleaned);
771
811
  renderBusView();
772
812
  return;
773
813
  }
package/src/code/tui.js CHANGED
@@ -18,6 +18,77 @@ const STATUS_INDICATORS = {
18
18
 
19
19
  const ANSI_PATTERN = /\x1B\[[0-9;?]*[ -/]*[@-~]/g;
20
20
 
21
+ function charDisplayWidth(char = "") {
22
+ if (!char) return 0;
23
+ const code = char.codePointAt(0) || 0;
24
+ if (code === 0) return 0;
25
+ if (code < 32 || (code >= 0x7f && code < 0xa0)) return 0;
26
+ if ((code >= 0x0300 && code <= 0x036f) ||
27
+ (code >= 0x1ab0 && code <= 0x1aff) ||
28
+ (code >= 0x1dc0 && code <= 0x1dff) ||
29
+ (code >= 0x20d0 && code <= 0x20ff) ||
30
+ (code >= 0xfe20 && code <= 0xfe2f)) {
31
+ return 0;
32
+ }
33
+ if ((code >= 0x1100 && code <= 0x115f) ||
34
+ code === 0x2329 ||
35
+ code === 0x232a ||
36
+ (code >= 0x2e80 && code <= 0xa4cf) ||
37
+ (code >= 0xac00 && code <= 0xd7a3) ||
38
+ (code >= 0xf900 && code <= 0xfaff) ||
39
+ (code >= 0xfe10 && code <= 0xfe19) ||
40
+ (code >= 0xfe30 && code <= 0xfe6f) ||
41
+ (code >= 0xff00 && code <= 0xff60) ||
42
+ (code >= 0xffe0 && code <= 0xffe6) ||
43
+ (code >= 0x1f300 && code <= 0x1faff)) {
44
+ return 2;
45
+ }
46
+ return 1;
47
+ }
48
+
49
+ function displayCellWidth(text = "") {
50
+ return Array.from(String(text || "").replace(ANSI_PATTERN, "")).reduce(
51
+ (sum, char) => sum + charDisplayWidth(char),
52
+ 0
53
+ );
54
+ }
55
+
56
+ function safeRead(getter, fallback = undefined) {
57
+ try {
58
+ return getter();
59
+ } catch {
60
+ return fallback;
61
+ }
62
+ }
63
+
64
+ function resolveLogContentWidth({ logBox = null, screen = null, fallback = 80 } = {}) {
65
+ const coords = safeRead(() => logBox && typeof logBox._getCoords === "function" ? logBox._getCoords() : null, null);
66
+ if (coords && Number.isFinite(coords.xl) && Number.isFinite(coords.xi)) {
67
+ return Math.max(1, coords.xl - coords.xi);
68
+ }
69
+ const width = safeRead(() => logBox && logBox.width, null);
70
+ if (typeof width === "number") return Math.max(1, width);
71
+ const screenWidth = safeRead(() => screen && screen.width, null);
72
+ if (typeof screenWidth === "number") return Math.max(1, screenWidth);
73
+ const screenCols = safeRead(() => screen && screen.cols, null);
74
+ if (typeof screenCols === "number") return Math.max(1, screenCols);
75
+ return Math.max(1, fallback);
76
+ }
77
+
78
+ function formatHighlightedUserInput(text = "", {
79
+ width = 80,
80
+ escapeText = (value) => String(value || ""),
81
+ } = {}) {
82
+ const plain = String(text || "").trim();
83
+ if (!plain) return "";
84
+ const targetWidth = Math.max(1, Math.floor(Number(width) || 80) - 1);
85
+ const prefix = " → ";
86
+ const suffix = " ";
87
+ const contentWidth = displayCellWidth(`${prefix}${plain}${suffix}`);
88
+ const pad = " ".repeat(Math.max(0, targetWidth - contentWidth));
89
+ return `{cyan-bg}{white-fg}${prefix}${escapeText(plain)}${suffix}${pad}{/white-fg}{/cyan-bg}`;
90
+ }
91
+
21
92
  // Stream buffer for smooth output
22
93
  class StreamBuffer {
23
94
  constructor(writer, options = {}) {
@@ -1027,13 +1098,12 @@ function runUcodeTui({
1027
1098
 
1028
1099
  const logUserInput = (text = "") => {
1029
1100
  activeToolMerge = null;
1030
- const plain = String(text || "").trim();
1031
- if (!plain) return;
1032
- const content = ` → ${escapeBlessed(plain)} `;
1033
- const visibleLen = plain.length + 4; // " → " + text + " "
1034
- const boxWidth = logBox.width || 80;
1035
- const pad = boxWidth > visibleLen ? " ".repeat(boxWidth - visibleLen) : "";
1036
- logBox.log(`{cyan-bg}{white-fg}${content}${pad}{/white-fg}{/cyan-bg}`);
1101
+ const line = formatHighlightedUserInput(text, {
1102
+ width: resolveLogContentWidth({ logBox, screen, fallback: (stdout && stdout.columns) || 80 }),
1103
+ escapeText: escapeBlessed,
1104
+ });
1105
+ if (!line) return;
1106
+ logBox.log(line);
1037
1107
  logBox.log(""); // Add line break after user input
1038
1108
  screen.render();
1039
1109
  };
@@ -1911,6 +1981,9 @@ module.exports = {
1911
1981
  UCODE_BANNER_LINES,
1912
1982
  UCODE_VERSION,
1913
1983
  StreamBuffer,
1984
+ displayCellWidth,
1985
+ resolveLogContentWidth,
1986
+ formatHighlightedUserInput,
1914
1987
  buildUcodeBannerLines,
1915
1988
  buildUcodeBannerBlessedLines,
1916
1989
  parseActiveAgentsFromBusStatus,