@ouro.bot/cli 0.1.0-alpha.178 → 0.1.0-alpha.179
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/changelog.json
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.179",
|
|
6
|
+
"changes": [
|
|
7
|
+
"feat(cli): generous vertical whitespace between TUI sections",
|
|
8
|
+
"feat(cli): image placeholder rendering (\ud83d\uddbc) for markdown images",
|
|
9
|
+
"feat(cli): Alt+Enter multi-line input with continuation prefix",
|
|
10
|
+
"feat(cli): terminal width safety (cap at 200, subtract margin)",
|
|
11
|
+
"fix(cli): settle/ponder/observe/rest fully hidden from imperative tool display"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
4
14
|
{
|
|
5
15
|
"version": "0.1.0-alpha.178",
|
|
6
16
|
"changes": [
|
|
@@ -40,30 +40,27 @@ function ringColor(elapsedSec) {
|
|
|
40
40
|
return OURO.scale;
|
|
41
41
|
}
|
|
42
42
|
// ─── Header ─────────────────────────────────────────────────────────
|
|
43
|
+
/** Safe terminal width: capped at 200, with 2-char margin */
|
|
44
|
+
function safeWidth() {
|
|
45
|
+
return Math.min(process.stdout.columns || 80, 200) - 2;
|
|
46
|
+
}
|
|
43
47
|
function Header({ agentName, model, contextPercent, cwd }) {
|
|
44
48
|
const showCtx = contextPercent > 0;
|
|
45
49
|
const info = [agentName, model, cwd, showCtx ? `ctx ${contextPercent}%` : ""].filter(Boolean).join(" · ");
|
|
46
50
|
// 3-segment snake: TAIL (fixed) + MIDDLE (stretches) + HEAD (fixed)
|
|
47
|
-
// From the reference art, segments split at the dots:
|
|
48
|
-
//
|
|
49
|
-
// . . ____
|
|
50
|
-
// __.__________________.____/ O \___/
|
|
51
|
-
// <__.__________________._________/ \
|
|
52
|
-
//
|
|
53
|
-
// TAIL: line1="" line2=" __" line3="<__"
|
|
54
|
-
// MIDDLE: line1=" " line2="_" line3="_" (or text)
|
|
55
|
-
// HEAD: line1=" ____" line2="____/ O \\___/" line3="_________/ \\"
|
|
56
51
|
const HEAD1 = " ____";
|
|
57
52
|
const HEAD2 = "____/ O \\___/";
|
|
58
53
|
const HEAD3 = "_________/ \\";
|
|
59
54
|
const TAIL1 = " ";
|
|
60
55
|
const TAIL2 = " __";
|
|
61
56
|
const TAIL3 = "<__";
|
|
62
|
-
const
|
|
63
|
-
const
|
|
57
|
+
const fixedWidth = TAIL1.length + HEAD1.length;
|
|
58
|
+
const maxMiddle = safeWidth() - fixedWidth;
|
|
59
|
+
const middleLen = Math.max(Math.min(Math.max(info.length, 20), maxMiddle), 10);
|
|
60
|
+
const textPad = Math.max(middleLen - info.length, 0);
|
|
64
61
|
const line1 = TAIL1 + " ".repeat(middleLen) + HEAD1;
|
|
65
62
|
const line2 = TAIL2 + "_".repeat(middleLen) + HEAD2;
|
|
66
|
-
const line3text = info + "_".repeat(textPad);
|
|
63
|
+
const line3text = (info.length > middleLen ? info.slice(0, middleLen) : info) + "_".repeat(textPad);
|
|
67
64
|
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: line1 }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: line2 }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.scale, children: [TAIL3, (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.glow, children: line3text }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: HEAD3 })] })] }));
|
|
68
65
|
}
|
|
69
66
|
// ─── Message Rendering ──────────────────────────────────────────────
|
|
@@ -78,17 +75,18 @@ function ToolResultLine({ tc }) {
|
|
|
78
75
|
function MessageBlock({ msg }) {
|
|
79
76
|
if (msg.role === "tool") {
|
|
80
77
|
const visibleCalls = msg.toolCalls?.filter(tc => !FLOW_CONTROL_TOOLS.has(tc.name));
|
|
78
|
+
// Flow control tools produce no visible output at all
|
|
81
79
|
if (!visibleCalls || visibleCalls.length === 0)
|
|
82
|
-
return (0, jsx_runtime_1.jsx)(ink_1.
|
|
80
|
+
return (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" });
|
|
83
81
|
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: visibleCalls.map((tc, i) => (0, jsx_runtime_1.jsx)(ToolResultLine, { tc: tc }, i)) }));
|
|
84
82
|
}
|
|
85
83
|
if (msg.role === "user") {
|
|
86
|
-
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: [msg.content ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, bold: true, children: msg.content }) : null, (0, jsx_runtime_1.jsx)(ink_1.Box, {
|
|
84
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: [msg.content ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, bold: true, children: msg.content }) : null, (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) })] }));
|
|
87
85
|
}
|
|
88
86
|
if (msg.role === "assistant") {
|
|
89
|
-
return ((0, jsx_runtime_1.
|
|
87
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginBottom: 1, children: msg.content ? (0, jsx_runtime_1.jsx)(streaming_markdown_1.StreamingMarkdown, { text: msg.content, maxWidth: safeWidth() }) : null }));
|
|
90
88
|
}
|
|
91
|
-
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: msg.content }), (0, jsx_runtime_1.jsx)(ink_1.Box, {
|
|
89
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginTop: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: msg.content }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) })] }));
|
|
92
90
|
}
|
|
93
91
|
// ─── Live Area (re-renders) ─────────────────────────────────────────
|
|
94
92
|
function Spinner({ phrasePool, elapsed }) {
|
|
@@ -136,12 +134,12 @@ function formatActiveToolArgs(name, args) {
|
|
|
136
134
|
function ActiveToolLine({ tool }) {
|
|
137
135
|
// Hide flow control tools from in-progress display
|
|
138
136
|
if (FLOW_CONTROL_TOOLS.has(tool.name))
|
|
139
|
-
return (0, jsx_runtime_1.jsx)(ink_1.
|
|
137
|
+
return (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" });
|
|
140
138
|
const argStr = formatActiveToolArgs(tool.name, tool.args);
|
|
141
139
|
return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: "∙" }), " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, children: tool.name }), " ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: argStr })] }));
|
|
142
140
|
}
|
|
143
141
|
function LiveArea({ live, elapsed }) {
|
|
144
|
-
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [live.streamingText ? ((0, jsx_runtime_1.jsx)(streaming_markdown_1.StreamingMarkdown, { text: live.streamingText })) : null, live.activeTool ? ((0, jsx_runtime_1.jsx)(ActiveToolLine, { tool: live.activeTool })) : null, live.loading ? ((0, jsx_runtime_1.jsx)(Spinner, { phrasePool: live.spinnerPhrasePool, elapsed: elapsed })) : null, live.errorMessage ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.fang, children: ["✗ ", live.errorMessage] })) : null, live.kickMessage ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.gold, children: ["↻ ", live.kickMessage] })) : null] }));
|
|
142
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [live.streamingText ? ((0, jsx_runtime_1.jsx)(streaming_markdown_1.StreamingMarkdown, { text: live.streamingText, maxWidth: safeWidth() })) : null, live.activeTool ? ((0, jsx_runtime_1.jsx)(ActiveToolLine, { tool: live.activeTool })) : null, live.loading ? ((0, jsx_runtime_1.jsx)(Spinner, { phrasePool: live.spinnerPhrasePool, elapsed: elapsed })) : null, live.errorMessage ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.fang, children: ["✗ ", live.errorMessage] })) : null, live.kickMessage ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: OURO.gold, children: ["↻ ", live.kickMessage] })) : null] }));
|
|
145
143
|
}
|
|
146
144
|
// ─── Input ──────────────────────────────────────────────────────────
|
|
147
145
|
function InputArea({ onSubmit, suppressed, onCtrlC, agentName, model, history }) {
|
|
@@ -194,6 +192,12 @@ function InputArea({ onSubmit, suppressed, onCtrlC, agentName, model, history })
|
|
|
194
192
|
return;
|
|
195
193
|
}
|
|
196
194
|
if (key.return) {
|
|
195
|
+
// Alt+Enter: insert newline instead of submitting
|
|
196
|
+
if (key.meta) {
|
|
197
|
+
inputRef.current += "\n";
|
|
198
|
+
setInput(inputRef.current);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
197
201
|
const text = inputRef.current;
|
|
198
202
|
if (text.trim())
|
|
199
203
|
onSubmit(text);
|
|
@@ -245,19 +249,21 @@ function InputArea({ onSubmit, suppressed, onCtrlC, agentName, model, history })
|
|
|
245
249
|
}
|
|
246
250
|
});
|
|
247
251
|
// Get terminal width (capped for sanity)
|
|
248
|
-
const cols =
|
|
252
|
+
const cols = safeWidth();
|
|
249
253
|
if (suppressed) {
|
|
250
254
|
// During model generation: show status bar but no input
|
|
251
255
|
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(cols) }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [agentName, model ? ` · ${model}` : ""] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(cols) })] }));
|
|
252
256
|
}
|
|
253
|
-
|
|
257
|
+
const isMultiline = input.includes("\n");
|
|
258
|
+
const inputLines = input.split("\n");
|
|
259
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(cols) }), isMultiline ? ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: inputLines.map((line, i) => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, bold: true, children: i === 0 ? ") " : "· " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: line }), i === inputLines.length - 1 ? (cursorVisible ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: "█" }) : (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " })) : null] }, i))) })) : ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, bold: true, children: ") " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: input }), cursorVisible ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.scale, children: "█" }) : (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " })] })), (0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [agentName, model ? ` · ${model}` : ""] }), (0, jsx_runtime_1.jsx)(ink_1.Box, { flexGrow: 1 }), tooltip ? (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: tooltip }) : null, isMultiline ? (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " (multi-line)" }) : null] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(cols) })] }));
|
|
254
260
|
}
|
|
255
261
|
// ─── Main TUI Component ─────────────────────────────────────────────
|
|
256
262
|
function OuroTui({ agentName, model, completedMessages, inputHistory, live, elapsedSeconds, contextPercent, onSubmit, onCtrlC, cwd, }) {
|
|
257
263
|
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Static, { items: [{ id: "__header__" }, ...completedMessages], children: (item, index) => {
|
|
258
264
|
if (index === 0) {
|
|
259
|
-
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: "
|
|
265
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginBottom: 2, children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }), (0, jsx_runtime_1.jsx)(Header, { agentName: agentName, model: model, contextPercent: contextPercent, cwd: cwd }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, dimColor: true, children: " Ctrl-C twice to exit \u00b7 \u2191\u2193 history \u00b7 Esc clear \u00b7 Alt+Enter newline" })] }, "header"));
|
|
260
266
|
}
|
|
261
267
|
return (0, jsx_runtime_1.jsx)(MessageBlock, { msg: item }, item.id);
|
|
262
|
-
} }), (live.loading || live.streamingText || live.activeTool) ? (0, jsx_runtime_1.jsx)(ink_1.Box, {
|
|
268
|
+
} }), (live.loading || live.streamingText || live.activeTool) ? (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }) : null, (0, jsx_runtime_1.jsx)(LiveArea, { live: live, elapsed: elapsedSeconds }), (live.loading || live.streamingText || live.activeTool) ? (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }) : null, (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }) }), (0, jsx_runtime_1.jsx)(InputArea, { onSubmit: onSubmit, suppressed: live.inputSuppressed, onCtrlC: onCtrlC, agentName: agentName, model: model, history: inputHistory })] }));
|
|
263
269
|
}
|
|
@@ -213,7 +213,21 @@ function parseInline(text) {
|
|
|
213
213
|
placeholders.push({ idx, segment: { text: restoreEscapes(code, escapes), code: true } });
|
|
214
214
|
return `\x00PH${idx}\x00`;
|
|
215
215
|
});
|
|
216
|
-
// Step
|
|
216
|
+
// Step 3a: extract image references  -> placeholder
|
|
217
|
+
processed = processed.replace(/!\[([^\]]*)\]\(([^)]*)\)/g, (_m, alt, _url) => {
|
|
218
|
+
const idx = placeholders.length;
|
|
219
|
+
const label = alt ? `\ud83d\uddbc ${restoreEscapes(alt, escapes)}` : "\ud83d\uddbc image";
|
|
220
|
+
placeholders.push({ idx, segment: { text: label, image: true } });
|
|
221
|
+
return `\x00PH${idx}\x00`;
|
|
222
|
+
});
|
|
223
|
+
// Step 3b: extract [Image: ...] references
|
|
224
|
+
processed = processed.replace(/\[Image:\s*([^\]]*)\]/gi, (_m, desc) => {
|
|
225
|
+
const idx = placeholders.length;
|
|
226
|
+
const label = desc.trim() ? `\ud83d\uddbc ${restoreEscapes(desc.trim(), escapes)}` : "\ud83d\uddbc image";
|
|
227
|
+
placeholders.push({ idx, segment: { text: label, image: true } });
|
|
228
|
+
return `\x00PH${idx}\x00`;
|
|
229
|
+
});
|
|
230
|
+
// Step 3c: extract links [text](url)
|
|
217
231
|
processed = processed.replace(/\[([^\]]*)\]\(([^)]*)\)/g, (_m, linkText, url) => {
|
|
218
232
|
const idx = placeholders.length;
|
|
219
233
|
const restoredText = restoreEscapes(linkText, escapes);
|
|
@@ -364,6 +378,9 @@ function wrapText(text, maxWidth) {
|
|
|
364
378
|
}
|
|
365
379
|
// ─── Segment Renderer ──────────────────────────────────────────────
|
|
366
380
|
function SegmentRenderer({ segment }) {
|
|
381
|
+
if (segment.image) {
|
|
382
|
+
return (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: segment.text });
|
|
383
|
+
}
|
|
367
384
|
if (segment.codeBlock) {
|
|
368
385
|
return (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: segment.text });
|
|
369
386
|
}
|
|
@@ -13,6 +13,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.writeToolStart = writeToolStart;
|
|
14
14
|
exports.writeToolEnd = writeToolEnd;
|
|
15
15
|
const runtime_1 = require("../../nerves/runtime");
|
|
16
|
+
// Flow control tools are invisible to the user — internal agent mechanics
|
|
17
|
+
const FLOW_CONTROL_TOOLS = new Set(["settle", "ponder", "observe", "rest"]);
|
|
16
18
|
// Ouroboros teal: #4ec9b0 -> RGB escape
|
|
17
19
|
const OURO_TEAL = "\x1b[38;2;78;201;176m";
|
|
18
20
|
const GREEN = "\x1b[32m";
|
|
@@ -56,6 +58,8 @@ function getPrimaryArg(name, args) {
|
|
|
56
58
|
* Shows: [teal tool name] [dim primary arg]
|
|
57
59
|
*/
|
|
58
60
|
function writeToolStart(name, args) {
|
|
61
|
+
if (FLOW_CONTROL_TOOLS.has(name))
|
|
62
|
+
return;
|
|
59
63
|
const primary = getPrimaryArg(name, args);
|
|
60
64
|
const argDisplay = primary ? ` ${DIM}${primary}${RESET}` : "";
|
|
61
65
|
process.stderr.write(`\r\x1b[K${OURO_TEAL}${BOLD}${name}${RESET}${argDisplay}\n`);
|
|
@@ -65,6 +69,8 @@ function writeToolStart(name, args) {
|
|
|
65
69
|
* Shows: [green check / red cross] [teal tool name] [dim summary]
|
|
66
70
|
*/
|
|
67
71
|
function writeToolEnd(name, argSummary, success) {
|
|
72
|
+
if (FLOW_CONTROL_TOOLS.has(name))
|
|
73
|
+
return;
|
|
68
74
|
const icon = success ? `${GREEN}\u2713` : `${RED}\u2717`;
|
|
69
75
|
const summary = argSummary ? ` ${DIM}${truncate(argSummary, 100)}${RESET}` : "";
|
|
70
76
|
process.stderr.write(`${icon}${RESET} ${OURO_TEAL}${name}${RESET}${summary}\n`);
|