u-foo 2.3.18 → 2.3.19

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.18",
3
+ "version": "2.3.19",
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",
@@ -1,6 +1,9 @@
1
1
  const os = require("os");
2
2
  const { version: packageVersion } = require("../../package.json");
3
3
 
4
+ const ANSI_RESET = "\x1b[0m";
5
+ const CLAUDE_ORANGE = "\x1b[38;2;217;119;87m";
6
+
4
7
  function createAgentViewController(options = {}) {
5
8
  const {
6
9
  screen,
@@ -55,6 +58,8 @@ function createAgentViewController(options = {}) {
55
58
  let busInputValue = "";
56
59
  let busInputCursor = 0;
57
60
  let busLogLines = [];
61
+ let busStartupAgentId = "";
62
+ let busStartupLineCount = 0;
58
63
  const originalRender = screen.render.bind(screen);
59
64
  let renderFrozen = false;
60
65
 
@@ -76,6 +81,10 @@ function createAgentViewController(options = {}) {
76
81
  .replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
77
82
  }
78
83
 
84
+ function hasAnsi(text = "") {
85
+ return /\x1b(?:\][^\x07\x1b]*(?:\x07|\x1b\\)|\[[0-9;?]*[ -/]*[@-~])/.test(String(text || ""));
86
+ }
87
+
79
88
  function clamp(value, min, max) {
80
89
  const normalized = Number.isFinite(value) ? Math.floor(value) : min;
81
90
  return Math.max(min, Math.min(max, normalized));
@@ -141,6 +150,39 @@ function createAgentViewController(options = {}) {
141
150
  return `${truncateToWidth(clean, normalizedWidth - 1).trimEnd()}…`;
142
151
  }
143
152
 
153
+ function fitAnsiText(text = "", width = 1) {
154
+ const normalizedWidth = Math.max(1, width);
155
+ const raw = String(text || "").replace(/\r/g, "");
156
+ if (!hasAnsi(raw)) return fitText(raw, normalizedWidth);
157
+ if (displayWidth(raw) <= normalizedWidth) {
158
+ return padToWidth(raw, normalizedWidth);
159
+ }
160
+ if (normalizedWidth <= 1) return "…";
161
+
162
+ const ansiPattern = /\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[ -/]*[@-~]/g;
163
+ let out = "";
164
+ let cells = 0;
165
+ let index = 0;
166
+ while (index < raw.length && cells < normalizedWidth - 1) {
167
+ ansiPattern.lastIndex = index;
168
+ const match = ansiPattern.exec(raw);
169
+ if (match && match.index === index) {
170
+ out += match[0];
171
+ index += match[0].length;
172
+ continue;
173
+ }
174
+ const char = Array.from(raw.slice(index))[0] || "";
175
+ if (!char) break;
176
+ const charWidth = charDisplayWidth(char);
177
+ if (cells + charWidth > normalizedWidth - 1) break;
178
+ out += char;
179
+ cells += charWidth;
180
+ index += char.length;
181
+ }
182
+ const suffix = `${ANSI_RESET}…`;
183
+ return padToWidth(`${out}${suffix}`, normalizedWidth);
184
+ }
185
+
144
186
  function horizontalLine(width = 80) {
145
187
  return "─".repeat(Math.max(1, width));
146
188
  }
@@ -149,6 +191,11 @@ function createAgentViewController(options = {}) {
149
191
  return fitText(text, Math.max(1, width));
150
192
  }
151
193
 
194
+ function logLine(text = "", width = 80) {
195
+ const normalizedWidth = Math.max(1, width);
196
+ return hasAnsi(text) ? fitAnsiText(text, normalizedWidth) : plainLine(text, normalizedWidth);
197
+ }
198
+
152
199
  function sliceDisplayCells(text = "", startCell = 0, maxCells = 1) {
153
200
  const targetStart = Math.max(0, startCell);
154
201
  const targetWidth = Math.max(1, maxCells);
@@ -174,6 +221,7 @@ function createAgentViewController(options = {}) {
174
221
 
175
222
  function wrapTextLine(text = "", width = 80) {
176
223
  const inner = Math.max(1, width);
224
+ if (hasAnsi(text)) return [String(text || "")];
177
225
  const clean = stripAnsi(String(text || ""));
178
226
  if (!clean) return [""];
179
227
  const lines = [];
@@ -247,16 +295,19 @@ function createAgentViewController(options = {}) {
247
295
  function buildClaudeStartupLines(agentLabel = "", width = 80) {
248
296
  const label = String(agentLabel || "").trim();
249
297
  const projectPath = compactProjectPath(getProjectRoot());
250
- const product = "Claude Code";
298
+ const product = "ClaudeCode";
251
299
  const detail = label ? `${label} · managed headless` : "managed headless";
300
+ const iconTop = `${CLAUDE_ORANGE}▐▛███▜▌${ANSI_RESET}`;
301
+ const iconMiddle = `${CLAUDE_ORANGE}▝▜█████▛▘${ANSI_RESET}`;
302
+ const iconBottom = `${CLAUDE_ORANGE}▘▘▝▝${ANSI_RESET}`;
252
303
  const lines = [
253
- ` ▐▛███▜▌${product} v${packageVersion}`,
254
- `▝▜█████▛▘${detail}`,
255
- ` ▘▘▝▝${projectPath}`,
304
+ ` ${iconTop} ${product}v${packageVersion}`,
305
+ `${iconMiddle}${detail}`,
306
+ ` ${iconBottom} ${projectPath}`,
256
307
  "",
257
308
  ];
258
309
  if (width < 44) return lines;
259
- return lines.map((line) => padToWidth(line, Math.min(58, Math.max(1, width))));
310
+ return lines.map((line) => fitAnsiText(line, Math.min(58, Math.max(1, width))));
260
311
  }
261
312
 
262
313
  function buildCodexStartupLines(agentLabel = "", width = 80) {
@@ -288,11 +339,31 @@ function createAgentViewController(options = {}) {
288
339
  return buildClaudeStartupLines(agentLabel || agentId, width);
289
340
  }
290
341
 
342
+ function staticStartupLines(agentId = "", agentLabel = "", width = 80) {
343
+ const lines = buildInternalStartupLines(agentId, agentLabel, width);
344
+ if (lines.length > 0 && String(lines[lines.length - 1] || "") === "") {
345
+ return lines.slice(0, -1);
346
+ }
347
+ return lines;
348
+ }
349
+
291
350
  function resetBusView(agentId) {
292
351
  busInputValue = "";
293
352
  busInputCursor = 0;
353
+ busStartupAgentId = agentId || "";
294
354
  const label = getAgentLabel(agentId);
295
- busLogLines = buildInternalStartupLines(agentId, label, getCols());
355
+ const startupLines = staticStartupLines(agentId, label, getCols());
356
+ busLogLines = startupLines.concat("");
357
+ busStartupLineCount = startupLines.length;
358
+ }
359
+
360
+ function refreshBusStartupLines(width = getCols()) {
361
+ if (!busStartupAgentId || busStartupLineCount <= 0) return;
362
+ const label = getAgentLabel(busStartupAgentId);
363
+ const startupLines = staticStartupLines(busStartupAgentId, label, width);
364
+ const tailLines = busLogLines.slice(busStartupLineCount);
365
+ busLogLines = startupLines.concat(tailLines.length > 0 ? tailLines : [""]);
366
+ busStartupLineCount = startupLines.length;
296
367
  }
297
368
 
298
369
  function appendBusLog(text = "") {
@@ -331,6 +402,7 @@ function createAgentViewController(options = {}) {
331
402
  const rows = getRows();
332
403
  const cols = getCols();
333
404
  const width = Math.max(20, cols);
405
+ refreshBusStartupLines(width);
334
406
  const inputTop = Math.max(4, rows - 3);
335
407
  const logContentTop = 1;
336
408
  const logContentBottom = Math.max(logContentTop, inputTop - 1);
@@ -339,7 +411,7 @@ function createAgentViewController(options = {}) {
339
411
  processStdout.write("\x1b[?25l");
340
412
  const visibleLines = getWrappedBusLogLines(width).slice(-logContentHeight);
341
413
  for (let i = 0; i < logContentHeight; i += 1) {
342
- writeAt(logContentTop + i, plainLine(visibleLines[i] || "", width));
414
+ writeAt(logContentTop + i, logLine(visibleLines[i] || "", width));
343
415
  }
344
416
 
345
417
  writeAt(inputTop, horizontalLine(width));
@@ -445,6 +517,8 @@ function createAgentViewController(options = {}) {
445
517
  busInputValue = "";
446
518
  busInputCursor = 0;
447
519
  busLogLines = [];
520
+ busStartupAgentId = "";
521
+ busStartupLineCount = 0;
448
522
 
449
523
  currentView = "main";
450
524
  viewingAgent = null;