u-foo 2.5.13 → 2.5.15
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/agents/prompts/native/environment.js +20 -8
- package/src/code/agent.js +339 -24
- package/src/code/commands.js +61 -0
- package/src/code/context/artifactGc.js +292 -0
- package/src/code/context/artifactIndex.js +161 -0
- package/src/code/context/artifacts.js +183 -0
- package/src/code/context/assembler.js +698 -0
- package/src/code/context/executionSegment.js +314 -0
- package/src/code/context/featureFlag.js +13 -0
- package/src/code/context/index.js +18 -0
- package/src/code/context/projectSnapshot.js +201 -0
- package/src/code/context/promptLayers.js +159 -0
- package/src/code/context/reducers.js +328 -0
- package/src/code/context/stableJson.js +29 -0
- package/src/code/context/stateCommit.js +412 -0
- package/src/code/context/transcript.js +182 -0
- package/src/code/context/transcriptSync.js +106 -0
- package/src/code/context/workingSet.js +323 -0
- package/src/code/dispatch.js +4 -1
- package/src/code/index.js +6 -0
- package/src/code/modelCommand.js +87 -0
- package/src/code/nativeRunner.js +187 -31
- package/src/code/repl.js +36 -32
- package/src/code/sessionStore.js +227 -15
- package/src/code/skills/index.js +10 -0
- package/src/code/skills/injection.js +65 -3
- package/src/code/skills/loader.js +21 -0
- package/src/code/skills/manifest.js +87 -0
- package/src/code/skills/render.js +15 -1
- package/src/code/taskDecomposer.js +32 -2
- package/src/code/tools/artifactRead.js +40 -0
- package/src/code/tui.js +2 -0
- package/src/code/usageStore.js +15 -0
- package/src/ui/format/index.js +260 -44
- package/src/ui/format/markdownRenderer.js +215 -72
- package/src/ui/ink/ChatApp.js +39 -8
- package/src/ui/ink/UcodeApp.js +408 -55
- package/src/ui/ink/chatLogModel.js +102 -21
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const { renderMarkdownLinesAnsi } = require("../format/markdownRenderer");
|
|
4
|
+
|
|
5
|
+
const MARKDOWN_BODY_KINDS = new Set(["assistant", "agent", "plain", "error", "success"]);
|
|
6
|
+
|
|
3
7
|
function stripBlessedTags(text = "") {
|
|
4
8
|
return String(text || "")
|
|
5
9
|
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "")
|
|
@@ -26,6 +30,39 @@ function compactDividerLabel(text = "") {
|
|
|
26
30
|
return label || String(text || "").trim() || "section";
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
function splitSpeakerBody(raw = "") {
|
|
34
|
+
const source = String(raw || "");
|
|
35
|
+
const dotIdx = source.indexOf(" · ");
|
|
36
|
+
if (dotIdx >= 0) {
|
|
37
|
+
return {
|
|
38
|
+
speaker: source.slice(0, dotIdx).trim(),
|
|
39
|
+
body: source.slice(dotIdx + 3),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const colonMatch = source.match(/^([A-Za-z0-9_.:@/-]{1,42}):\s+(.*)$/);
|
|
43
|
+
if (colonMatch) {
|
|
44
|
+
return { speaker: colonMatch[1], body: colonMatch[2] || "" };
|
|
45
|
+
}
|
|
46
|
+
return { speaker: "", body: source };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function formatChatLogBody(body = "", kind = "plain", markdownState = null) {
|
|
50
|
+
const text = String(body || "");
|
|
51
|
+
if (!MARKDOWN_BODY_KINDS.has(kind)) return text;
|
|
52
|
+
// Structural markers / empty rows stay untouched.
|
|
53
|
+
if (!text.trim()) return text;
|
|
54
|
+
try {
|
|
55
|
+
const state = markdownState && typeof markdownState === "object"
|
|
56
|
+
? markdownState
|
|
57
|
+
: { inCodeBlock: false };
|
|
58
|
+
const lines = renderMarkdownLinesAnsi(text, state);
|
|
59
|
+
if (!Array.isArray(lines) || lines.length === 0) return text;
|
|
60
|
+
return lines.length === 1 ? lines[0] : lines.join("\n");
|
|
61
|
+
} catch {
|
|
62
|
+
return text;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
29
66
|
function classifyChatLogLine(text = "") {
|
|
30
67
|
const raw = stripBlessedTags(text).replace(/\r/g, "");
|
|
31
68
|
const clean = stripMarkdownDecorators(raw);
|
|
@@ -41,23 +78,47 @@ function classifyChatLogLine(text = "") {
|
|
|
41
78
|
return { kind: "meta", marker: "·", speaker: "", body: clean };
|
|
42
79
|
}
|
|
43
80
|
if (/^(error:|✗|failed\b)/i.test(trimmed)) {
|
|
44
|
-
|
|
81
|
+
const rawBody = raw.replace(/^(error:\s*)/i, "");
|
|
82
|
+
return {
|
|
83
|
+
kind: "error",
|
|
84
|
+
marker: "!",
|
|
85
|
+
speaker: "error",
|
|
86
|
+
body: rawBody || clean.replace(/^(error:\s*)/i, ""),
|
|
87
|
+
};
|
|
45
88
|
}
|
|
46
89
|
if (/^(✓|✔|done\b|closed\b)/i.test(trimmed)) {
|
|
47
|
-
|
|
90
|
+
const rawBody = raw.replace(/^[✓✔]\s*/, "");
|
|
91
|
+
return {
|
|
92
|
+
kind: "success",
|
|
93
|
+
marker: "✓",
|
|
94
|
+
speaker: "",
|
|
95
|
+
body: rawBody || clean.replace(/^[✓✔]\s*/, ""),
|
|
96
|
+
};
|
|
48
97
|
}
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
51
|
-
const
|
|
98
|
+
const cleanDot = clean.match(/^([^·\n]{1,64})\s+·\s+(.*)$/);
|
|
99
|
+
if (cleanDot) {
|
|
100
|
+
const parts = splitSpeakerBody(raw);
|
|
101
|
+
const speaker = stripMarkdownDecorators(parts.speaker || cleanDot[1]).trim();
|
|
52
102
|
const lower = speaker.toLowerCase();
|
|
53
103
|
const kind = lower === "ufoo" ? "assistant" : "agent";
|
|
54
|
-
return {
|
|
104
|
+
return {
|
|
105
|
+
kind,
|
|
106
|
+
marker: kind === "assistant" ? "◆" : "•",
|
|
107
|
+
speaker,
|
|
108
|
+
body: parts.body != null ? parts.body : (cleanDot[2] || " "),
|
|
109
|
+
};
|
|
55
110
|
}
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
|
|
111
|
+
const cleanColon = clean.match(/^([A-Za-z0-9_.:@/-]{1,42}):\s+(.*)$/);
|
|
112
|
+
if (cleanColon) {
|
|
113
|
+
const parts = splitSpeakerBody(raw);
|
|
114
|
+
return {
|
|
115
|
+
kind: "agent",
|
|
116
|
+
marker: "•",
|
|
117
|
+
speaker: stripMarkdownDecorators(parts.speaker || cleanColon[1]).trim(),
|
|
118
|
+
body: parts.body != null ? parts.body : (cleanColon[2] || " "),
|
|
119
|
+
};
|
|
59
120
|
}
|
|
60
|
-
return { kind: "plain", marker: "", speaker: "", body: clean };
|
|
121
|
+
return { kind: "plain", marker: "", speaker: "", body: raw || clean };
|
|
61
122
|
}
|
|
62
123
|
|
|
63
124
|
function defaultMarkerForKind(kind = "", speaker = "") {
|
|
@@ -71,12 +132,19 @@ function defaultMarkerForKind(kind = "", speaker = "") {
|
|
|
71
132
|
return speaker ? "•" : "";
|
|
72
133
|
}
|
|
73
134
|
|
|
74
|
-
function buildChatLogLineModel(input = "") {
|
|
135
|
+
function buildChatLogLineModel(input = "", options = {}) {
|
|
136
|
+
const markdownState = options && options.markdownState;
|
|
137
|
+
|
|
75
138
|
if (input && typeof input === "object" && !input.kind) {
|
|
76
|
-
return buildChatLogLineModel(chatLogEntryText(input));
|
|
139
|
+
return buildChatLogLineModel(chatLogEntryText(input), options);
|
|
77
140
|
}
|
|
78
141
|
|
|
79
142
|
if (input && typeof input === "object" && input.kind) {
|
|
143
|
+
// Prefer original text when present so markdown can be (re)applied with
|
|
144
|
+
// a shared fence state — e.g. Static decoration.
|
|
145
|
+
if (input.text != null && String(input.text).length > 0 && markdownState) {
|
|
146
|
+
return buildChatLogLineModel(String(input.text), options);
|
|
147
|
+
}
|
|
80
148
|
const kind = String(input.kind || "plain");
|
|
81
149
|
const speaker = String(input.speaker || "");
|
|
82
150
|
const marker = input.marker != null ? String(input.marker) : defaultMarkerForKind(kind, speaker);
|
|
@@ -84,6 +152,9 @@ function buildChatLogLineModel(input = "") {
|
|
|
84
152
|
? String(input.bodyText)
|
|
85
153
|
: String(input.body != null ? input.body : chatLogEntryText(input));
|
|
86
154
|
const body = kind === "plain" ? compactContinuationIndent(rawBody || " ") : (rawBody || " ");
|
|
155
|
+
const bodyText = markdownState
|
|
156
|
+
? formatChatLogBody(body, kind, markdownState)
|
|
157
|
+
: body;
|
|
87
158
|
return {
|
|
88
159
|
kind,
|
|
89
160
|
marker,
|
|
@@ -92,7 +163,7 @@ function buildChatLogLineModel(input = "") {
|
|
|
92
163
|
markerText: input.markerText != null
|
|
93
164
|
? String(input.markerText)
|
|
94
165
|
: (speaker ? `${marker || " "} ` : `${marker || " "} `),
|
|
95
|
-
bodyText
|
|
166
|
+
bodyText,
|
|
96
167
|
};
|
|
97
168
|
}
|
|
98
169
|
|
|
@@ -104,7 +175,7 @@ function buildChatLogLineModel(input = "") {
|
|
|
104
175
|
return {
|
|
105
176
|
...row,
|
|
106
177
|
markerText: hasSpeaker ? `${row.marker || " "} ` : `${row.marker || " "} `,
|
|
107
|
-
bodyText: body,
|
|
178
|
+
bodyText: formatChatLogBody(body, row.kind, markdownState || { inCodeBlock: false }),
|
|
108
179
|
};
|
|
109
180
|
}
|
|
110
181
|
|
|
@@ -113,12 +184,15 @@ function normalizeEntryInput(input) {
|
|
|
113
184
|
return { text: input };
|
|
114
185
|
}
|
|
115
186
|
|
|
116
|
-
function createChatLogEntry(input = "", id = "") {
|
|
187
|
+
function createChatLogEntry(input = "", id = "", options = {}) {
|
|
117
188
|
const source = normalizeEntryInput(input);
|
|
118
189
|
const text = String(source.text != null ? source.text : chatLogEntryText(source));
|
|
119
|
-
const
|
|
120
|
-
?
|
|
121
|
-
:
|
|
190
|
+
const markdownState = options && options.markdownState
|
|
191
|
+
? options.markdownState
|
|
192
|
+
: { inCodeBlock: false };
|
|
193
|
+
const row = source.kind && !(options && options.markdownState)
|
|
194
|
+
? buildChatLogLineModel({ ...source, text }, { markdownState })
|
|
195
|
+
: buildChatLogLineModel(text, { markdownState });
|
|
122
196
|
const meta = source.meta && typeof source.meta === "object" && !Array.isArray(source.meta)
|
|
123
197
|
? { ...source.meta }
|
|
124
198
|
: {};
|
|
@@ -160,13 +234,17 @@ function buildChatLogGroups(items = []) {
|
|
|
160
234
|
const source = Array.isArray(items) ? items : [];
|
|
161
235
|
const groups = [];
|
|
162
236
|
let current = null;
|
|
237
|
+
const markdownState = { inCodeBlock: false };
|
|
163
238
|
for (let index = 0; index < source.length; index += 1) {
|
|
164
239
|
const item = source[index] || {};
|
|
165
240
|
const itemId = item && typeof item === "object" && item.id ? item.id : `log-${index}`;
|
|
166
|
-
const
|
|
241
|
+
const text = item && typeof item === "object" && item.text != null
|
|
242
|
+
? String(item.text)
|
|
243
|
+
: chatLogEntryText(item);
|
|
244
|
+
const row = buildChatLogLineModel(text, { markdownState });
|
|
167
245
|
const entry = {
|
|
168
246
|
id: itemId,
|
|
169
|
-
text
|
|
247
|
+
text,
|
|
170
248
|
row,
|
|
171
249
|
sourceType: item && typeof item === "object" ? String(item.sourceType || item.type || "") : "",
|
|
172
250
|
meta: item && typeof item === "object" && item.meta ? item.meta : {},
|
|
@@ -196,7 +274,10 @@ module.exports = {
|
|
|
196
274
|
compactDividerLabel,
|
|
197
275
|
classifyChatLogLine,
|
|
198
276
|
buildChatLogLineModel,
|
|
199
|
-
|
|
277
|
+
formatChatLogBody,
|
|
200
278
|
createChatLogEntry,
|
|
201
279
|
chatLogEntryText,
|
|
280
|
+
canAppendToChatLogGroup,
|
|
281
|
+
buildChatLogGroups,
|
|
282
|
+
MARKDOWN_BODY_KINDS,
|
|
202
283
|
};
|