u-foo 2.5.15 → 3.0.0
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 +1 -1
- package/src/code/agent.js +333 -243
- package/src/code/commands.js +16 -0
- package/src/code/context/assembler.js +18 -13
- package/src/code/context/executionSegment.js +97 -119
- package/src/code/context/index.js +11 -1
- package/src/code/context/planGraph.js +1410 -0
- package/src/code/context/planGraphService.js +857 -0
- package/src/code/context/planMode.js +398 -0
- package/src/code/context/planProjection.js +432 -0
- package/src/code/context/promptLayers.js +21 -5
- package/src/code/context/stateCommit.js +2 -0
- package/src/code/context/toolRuntime.js +172 -0
- package/src/code/context/userInteraction.js +457 -0
- package/src/code/context/userNudge.js +116 -0
- package/src/code/dispatch.js +17 -1
- package/src/code/index.js +2 -0
- package/src/code/nativeRunner.js +518 -37
- package/src/code/repl.js +160 -18
- package/src/code/runtime/agentWakeup.js +58 -0
- package/src/code/runtime/graphOwner.js +41 -0
- package/src/code/runtime/graphYieldRouter.js +42 -0
- package/src/code/runtime/index.js +15 -0
- package/src/code/runtime/loopMailbox.js +124 -0
- package/src/code/runtime/runtimeEvents.js +39 -0
- package/src/code/runtime/taskControl.js +565 -0
- package/src/code/runtime/taskFocus.js +165 -0
- package/src/code/runtime/taskLoop.js +383 -0
- package/src/code/runtime/taskRun.js +187 -0
- package/src/code/runtime/toolProvenance.js +70 -0
- package/src/code/runtime/workspaceLease.js +208 -0
- package/src/code/sessionStore.js +0 -10
- package/src/code/skills/injection.js +1 -0
- package/src/code/taskDecomposer.js +32 -8
- package/src/code/tools/askUser.js +11 -0
- package/src/code/tools/planGraph.js +29 -0
- package/src/ui/format/index.js +25 -1
- package/src/ui/format/markdownRenderer.js +224 -2
- package/src/ui/ink/UcodeApp.js +285 -22
- package/src/code/context/featureFlag.js +0 -13
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Produces either blessed tags (chat / legacy) or chalk ANSI (Ink ucode).
|
|
5
5
|
* Terminals cannot change font size, so headings/emphasis use color + weight
|
|
6
|
-
* instead of literal `#` / `**` markers.
|
|
6
|
+
* instead of literal `#` / `**` markers. GFM pipe tables are aligned into a
|
|
7
|
+
* compact spreadsheet-like grid.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
const chalk = require("chalk");
|
|
@@ -15,6 +16,116 @@ function stripLeakedEscapeTags(text = "") {
|
|
|
15
16
|
return withoutDanglingEscape.replace(/\{\s*\/?\s*e?s?c?a?p?e?[^{}\n]*$/gi, "");
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
/** Visible width for CJK-aware table padding (no ANSI). */
|
|
20
|
+
function visibleWidth(text = "") {
|
|
21
|
+
let width = 0;
|
|
22
|
+
for (const char of String(text || "")) {
|
|
23
|
+
const code = char.codePointAt(0) || 0;
|
|
24
|
+
if (code < 32 || (code >= 0x7f && code < 0xa0)) continue;
|
|
25
|
+
if (
|
|
26
|
+
(code >= 0x1100 && code <= 0x115f)
|
|
27
|
+
|| code === 0x2329
|
|
28
|
+
|| code === 0x232a
|
|
29
|
+
|| (code >= 0x2e80 && code <= 0xa4cf)
|
|
30
|
+
|| (code >= 0xac00 && code <= 0xd7a3)
|
|
31
|
+
|| (code >= 0xf900 && code <= 0xfaff)
|
|
32
|
+
|| (code >= 0xfe10 && code <= 0xfe19)
|
|
33
|
+
|| (code >= 0xfe30 && code <= 0xfe6f)
|
|
34
|
+
|| (code >= 0xff00 && code <= 0xff60)
|
|
35
|
+
|| (code >= 0xffe0 && code <= 0xffe6)
|
|
36
|
+
) {
|
|
37
|
+
width += 2;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
width += 1;
|
|
41
|
+
}
|
|
42
|
+
return width;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isTableSeparatorLine(line = "") {
|
|
46
|
+
const raw = String(line || "").trim();
|
|
47
|
+
if (!raw.includes("-")) return false;
|
|
48
|
+
return /^\|?(\s*:?-+:?\s*\|)+\s*:?-+:?\s*\|?$/.test(raw);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isTableRowLine(line = "") {
|
|
52
|
+
const raw = String(line || "").trim();
|
|
53
|
+
if (!raw.includes("|")) return false;
|
|
54
|
+
if (isTableSeparatorLine(raw)) return true;
|
|
55
|
+
// Prefer GFM pipe rows (`| a | b |`). Also allow compact `a | b | c`
|
|
56
|
+
// (at least two pipes). Lone prose like `A | B` is not a table row.
|
|
57
|
+
if (/^\|.*\|$/.test(raw)) return true;
|
|
58
|
+
const parts = raw.split("|");
|
|
59
|
+
return parts.length >= 3 && parts.some((p) => p.trim().length > 0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Buffers consecutive GFM table rows so they can be rendered as one block
|
|
64
|
+
* (column widths need the full table). Call flush() before non-table output.
|
|
65
|
+
*/
|
|
66
|
+
function createMarkdownTableBuffer() {
|
|
67
|
+
const rows = [];
|
|
68
|
+
return {
|
|
69
|
+
get size() {
|
|
70
|
+
return rows.length;
|
|
71
|
+
},
|
|
72
|
+
push(line = "") {
|
|
73
|
+
const raw = String(line == null ? "" : line);
|
|
74
|
+
if (!isTableRowLine(raw)) return false;
|
|
75
|
+
rows.push(raw);
|
|
76
|
+
return true;
|
|
77
|
+
},
|
|
78
|
+
flush() {
|
|
79
|
+
if (rows.length === 0) return null;
|
|
80
|
+
const text = rows.join("\n");
|
|
81
|
+
rows.length = 0;
|
|
82
|
+
return text;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseTableCells(line = "") {
|
|
88
|
+
let raw = String(line || "").trim();
|
|
89
|
+
if (raw.startsWith("|")) raw = raw.slice(1);
|
|
90
|
+
if (raw.endsWith("|")) raw = raw.slice(0, -1);
|
|
91
|
+
return raw.split("|").map((cell) => cell.trim());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseSeparatorAlignments(line = "", columnCount = 0) {
|
|
95
|
+
const cells = parseTableCells(line);
|
|
96
|
+
const aligns = cells.map((cell) => {
|
|
97
|
+
const left = cell.startsWith(":");
|
|
98
|
+
const right = cell.endsWith(":");
|
|
99
|
+
if (left && right) return "center";
|
|
100
|
+
if (right) return "right";
|
|
101
|
+
return "left";
|
|
102
|
+
});
|
|
103
|
+
while (aligns.length < columnCount) aligns.push("left");
|
|
104
|
+
return aligns.slice(0, Math.max(columnCount, aligns.length));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function alignCell(text = "", width = 0, align = "left") {
|
|
108
|
+
const value = String(text || "");
|
|
109
|
+
const current = visibleWidth(value);
|
|
110
|
+
if (current >= width) return value;
|
|
111
|
+
const pad = width - current;
|
|
112
|
+
if (align === "right") return `${" ".repeat(pad)}${value}`;
|
|
113
|
+
if (align === "center") {
|
|
114
|
+
const left = Math.floor(pad / 2);
|
|
115
|
+
const right = pad - left;
|
|
116
|
+
return `${" ".repeat(left)}${value}${" ".repeat(right)}`;
|
|
117
|
+
}
|
|
118
|
+
return `${value}${" ".repeat(pad)}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function plainCellWidth(cell = "") {
|
|
122
|
+
const plain = String(cell || "")
|
|
123
|
+
.replace(/\*\*|__/g, "")
|
|
124
|
+
.replace(/`/g, "")
|
|
125
|
+
.replace(/\*/g, "");
|
|
126
|
+
return visibleWidth(plain);
|
|
127
|
+
}
|
|
128
|
+
|
|
18
129
|
function createBlessedAdapters(escapeFn = (value) => String(value || "")) {
|
|
19
130
|
const escape = (value) => escapeFn(value);
|
|
20
131
|
return {
|
|
@@ -40,6 +151,11 @@ function createBlessedAdapters(escapeFn = (value) => String(value || "")) {
|
|
|
40
151
|
fenceClose: () => "{gray-fg}└{/gray-fg}",
|
|
41
152
|
fenceBody: (value) => `{gray-fg}│{/gray-fg} {white-fg}${escape(value)}{/white-fg}`,
|
|
42
153
|
error: (value) => `{red-fg}${value}{/red-fg}`,
|
|
154
|
+
tablePipe: () => "{gray-fg}│{/gray-fg}",
|
|
155
|
+
tableSepCross: () => "{gray-fg}┼{/gray-fg}",
|
|
156
|
+
tableSepH: () => "{gray-fg}─{/gray-fg}",
|
|
157
|
+
tableHeaderCell: (value) => `{bold}{white-fg}${value}{/white-fg}{/bold}`,
|
|
158
|
+
tableCell: (value) => value,
|
|
43
159
|
};
|
|
44
160
|
}
|
|
45
161
|
|
|
@@ -76,6 +192,11 @@ function createAnsiAdapters() {
|
|
|
76
192
|
fenceClose: () => paint.gray("└"),
|
|
77
193
|
fenceBody: (value) => `${paint.gray("│")} ${paint.white(String(value || ""))}`,
|
|
78
194
|
error: (value) => paint.red(String(value || "")),
|
|
195
|
+
tablePipe: () => paint.gray("│"),
|
|
196
|
+
tableSepCross: () => paint.gray("┼"),
|
|
197
|
+
tableSepH: () => paint.gray("─"),
|
|
198
|
+
tableHeaderCell: (value) => paint.bold.whiteBright(String(value || "")),
|
|
199
|
+
tableCell: (value) => String(value || ""),
|
|
79
200
|
};
|
|
80
201
|
}
|
|
81
202
|
|
|
@@ -161,6 +282,82 @@ function renderInlineMarkdown(input = "", adapters = createBlessedAdapters()) {
|
|
|
161
282
|
return out;
|
|
162
283
|
}
|
|
163
284
|
|
|
285
|
+
function renderTableBlock(rawRows = [], adapters = createBlessedAdapters()) {
|
|
286
|
+
if (!Array.isArray(rawRows) || rawRows.length === 0) return [];
|
|
287
|
+
|
|
288
|
+
const rows = [];
|
|
289
|
+
let alignments = [];
|
|
290
|
+
let headerUsed = false;
|
|
291
|
+
|
|
292
|
+
for (let i = 0; i < rawRows.length; i += 1) {
|
|
293
|
+
const line = rawRows[i];
|
|
294
|
+
if (isTableSeparatorLine(line)) {
|
|
295
|
+
if (rows.length > 0 && !headerUsed) {
|
|
296
|
+
rows[rows.length - 1].isHeader = true;
|
|
297
|
+
headerUsed = true;
|
|
298
|
+
}
|
|
299
|
+
alignments = parseSeparatorAlignments(line, Math.max(alignments.length, parseTableCells(line).length));
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
rows.push({
|
|
303
|
+
cells: parseTableCells(line),
|
|
304
|
+
isHeader: false,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (rows.length === 0) return [];
|
|
309
|
+
|
|
310
|
+
const columnCount = rows.reduce((max, row) => Math.max(max, row.cells.length), 0);
|
|
311
|
+
while (alignments.length < columnCount) alignments.push("left");
|
|
312
|
+
|
|
313
|
+
const widths = Array.from({ length: columnCount }, () => 1);
|
|
314
|
+
for (const row of rows) {
|
|
315
|
+
for (let c = 0; c < columnCount; c += 1) {
|
|
316
|
+
widths[c] = Math.max(widths[c], plainCellWidth(row.cells[c] || ""));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const pipe = adapters.tablePipe || (() => "│");
|
|
321
|
+
const sepH = adapters.tableSepH || (() => "─");
|
|
322
|
+
const sepCross = adapters.tableSepCross || (() => "┼");
|
|
323
|
+
const styleHeader = adapters.tableHeaderCell || ((v) => v);
|
|
324
|
+
const styleCell = adapters.tableCell || ((v) => v);
|
|
325
|
+
|
|
326
|
+
const out = [];
|
|
327
|
+
let wroteHeaderSep = false;
|
|
328
|
+
|
|
329
|
+
for (const row of rows) {
|
|
330
|
+
const renderedCells = [];
|
|
331
|
+
for (let c = 0; c < columnCount; c += 1) {
|
|
332
|
+
const rawCell = row.cells[c] || "";
|
|
333
|
+
const inline = renderInlineMarkdown(rawCell, adapters);
|
|
334
|
+
const plain = String(rawCell)
|
|
335
|
+
.replace(/\*\*|__/g, "")
|
|
336
|
+
.replace(/`/g, "")
|
|
337
|
+
.replace(/\*/g, "");
|
|
338
|
+
const alignedPlain = alignCell(plain, widths[c], alignments[c] || "left");
|
|
339
|
+
const padRight = Math.max(0, visibleWidth(alignedPlain) - visibleWidth(plain));
|
|
340
|
+
const styled = row.isHeader ? styleHeader(inline) : styleCell(inline);
|
|
341
|
+
renderedCells.push(`${styled}${" ".repeat(padRight)}`);
|
|
342
|
+
}
|
|
343
|
+
out.push(`${pipe()} ${renderedCells.join(` ${pipe()} `)} ${pipe()}`);
|
|
344
|
+
|
|
345
|
+
if (row.isHeader && !wroteHeaderSep) {
|
|
346
|
+
const segments = widths.map((w) => {
|
|
347
|
+
const unit = sepH();
|
|
348
|
+
// sepH may be a styled string longer than 1 codepoint; repeat by width.
|
|
349
|
+
return unit.repeat(Math.max(1, w));
|
|
350
|
+
});
|
|
351
|
+
out.push(
|
|
352
|
+
`${pipe()}${sepH()}${segments.join(`${sepH()}${sepCross()}${sepH()}`)}${sepH()}${pipe()}`,
|
|
353
|
+
);
|
|
354
|
+
wroteHeaderSep = true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return out;
|
|
359
|
+
}
|
|
360
|
+
|
|
164
361
|
function renderMarkdownLinesWithAdapters(text = "", state = {}, adapters = createBlessedAdapters()) {
|
|
165
362
|
const renderState = state && typeof state === "object" ? state : {};
|
|
166
363
|
if (typeof renderState.inCodeBlock !== "boolean") {
|
|
@@ -170,7 +367,8 @@ function renderMarkdownLinesWithAdapters(text = "", state = {}, adapters = creat
|
|
|
170
367
|
const lines = String(text || "").split(/\r?\n/);
|
|
171
368
|
const out = [];
|
|
172
369
|
|
|
173
|
-
for (
|
|
370
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
371
|
+
const line = lines[index];
|
|
174
372
|
const raw = stripLeakedEscapeTags(String(line || ""));
|
|
175
373
|
const fenceMatch = raw.match(/^(\s*)(`{3,}|~{3,})(.*)$/);
|
|
176
374
|
if (fenceMatch) {
|
|
@@ -190,6 +388,24 @@ function renderMarkdownLinesWithAdapters(text = "", state = {}, adapters = creat
|
|
|
190
388
|
continue;
|
|
191
389
|
}
|
|
192
390
|
|
|
391
|
+
// GFM table block — collect contiguous pipe rows for column alignment.
|
|
392
|
+
if (isTableRowLine(raw)) {
|
|
393
|
+
const block = [raw];
|
|
394
|
+
let look = index + 1;
|
|
395
|
+
while (look < lines.length) {
|
|
396
|
+
const nextRaw = stripLeakedEscapeTags(String(lines[look] || ""));
|
|
397
|
+
if (!isTableRowLine(nextRaw)) break;
|
|
398
|
+
block.push(nextRaw);
|
|
399
|
+
look += 1;
|
|
400
|
+
}
|
|
401
|
+
const hasSep = block.some((row) => isTableSeparatorLine(row));
|
|
402
|
+
if (hasSep || block.length >= 2) {
|
|
403
|
+
out.push(...renderTableBlock(block, adapters));
|
|
404
|
+
index = look - 1;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
193
409
|
if (/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(raw)) {
|
|
194
410
|
out.push(adapters.rule());
|
|
195
411
|
continue;
|
|
@@ -261,4 +477,10 @@ module.exports = {
|
|
|
261
477
|
renderMarkdownLinesWithAdapters,
|
|
262
478
|
createBlessedAdapters,
|
|
263
479
|
createAnsiAdapters,
|
|
480
|
+
isTableRowLine,
|
|
481
|
+
isTableSeparatorLine,
|
|
482
|
+
parseTableCells,
|
|
483
|
+
renderTableBlock,
|
|
484
|
+
createMarkdownTableBuffer,
|
|
485
|
+
visibleWidth,
|
|
264
486
|
};
|