praana 0.5.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/LICENSE +21 -0
- package/README.md +124 -0
- package/bin/praana.js +17 -0
- package/bin/pran.js +17 -0
- package/dist/app-banner.d.ts +11 -0
- package/dist/app-banner.js +161 -0
- package/dist/app-controller.d.ts +44 -0
- package/dist/app-controller.js +143 -0
- package/dist/app-identity.d.ts +18 -0
- package/dist/app-identity.js +52 -0
- package/dist/auto-compact.d.ts +16 -0
- package/dist/auto-compact.js +101 -0
- package/dist/cli-args.d.ts +14 -0
- package/dist/cli-args.js +69 -0
- package/dist/compile-classic.d.ts +21 -0
- package/dist/compile-classic.js +106 -0
- package/dist/compiler.d.ts +75 -0
- package/dist/compiler.js +406 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +433 -0
- package/dist/context-engine/activity-log.d.ts +9 -0
- package/dist/context-engine/activity-log.js +109 -0
- package/dist/context-engine/artifact-store.d.ts +32 -0
- package/dist/context-engine/artifact-store.js +272 -0
- package/dist/context-engine/bm25.d.ts +3 -0
- package/dist/context-engine/bm25.js +32 -0
- package/dist/context-engine/checkpoint.d.ts +34 -0
- package/dist/context-engine/checkpoint.js +430 -0
- package/dist/context-engine/classify.d.ts +3 -0
- package/dist/context-engine/classify.js +60 -0
- package/dist/context-engine/db.d.ts +73 -0
- package/dist/context-engine/db.js +505 -0
- package/dist/context-engine/distiller.d.ts +30 -0
- package/dist/context-engine/distiller.js +67 -0
- package/dist/context-engine/engine-compiler.d.ts +23 -0
- package/dist/context-engine/engine-compiler.js +297 -0
- package/dist/context-engine/error-tracker.d.ts +21 -0
- package/dist/context-engine/error-tracker.js +74 -0
- package/dist/context-engine/event-lineage.d.ts +26 -0
- package/dist/context-engine/event-lineage.js +120 -0
- package/dist/context-engine/extraction.d.ts +26 -0
- package/dist/context-engine/extraction.js +83 -0
- package/dist/context-engine/index.d.ts +82 -0
- package/dist/context-engine/index.js +238 -0
- package/dist/context-engine/scoring.d.ts +13 -0
- package/dist/context-engine/scoring.js +47 -0
- package/dist/context-engine/state-snapshot.d.ts +8 -0
- package/dist/context-engine/state-snapshot.js +50 -0
- package/dist/context-engine/summarize.d.ts +6 -0
- package/dist/context-engine/summarize.js +32 -0
- package/dist/context-engine/telemetry.d.ts +25 -0
- package/dist/context-engine/telemetry.js +64 -0
- package/dist/context-engine/turn-digest.d.ts +50 -0
- package/dist/context-engine/turn-digest.js +250 -0
- package/dist/context-engine/turn-ledger.d.ts +18 -0
- package/dist/context-engine/turn-ledger.js +184 -0
- package/dist/context-engine/turn-recorder.d.ts +24 -0
- package/dist/context-engine/turn-recorder.js +88 -0
- package/dist/context-engine/types.d.ts +201 -0
- package/dist/context-engine/types.js +4 -0
- package/dist/context-pressure.d.ts +19 -0
- package/dist/context-pressure.js +36 -0
- package/dist/distillers/generic.d.ts +14 -0
- package/dist/distillers/generic.js +93 -0
- package/dist/distillers/git-diff.d.ts +8 -0
- package/dist/distillers/git-diff.js +119 -0
- package/dist/distillers/index.d.ts +2 -0
- package/dist/distillers/index.js +16 -0
- package/dist/distillers/npm-test.d.ts +8 -0
- package/dist/distillers/npm-test.js +50 -0
- package/dist/distillers/rg-results.d.ts +8 -0
- package/dist/distillers/rg-results.js +28 -0
- package/dist/distillers/tsc-errors.d.ts +8 -0
- package/dist/distillers/tsc-errors.js +52 -0
- package/dist/event-log.d.ts +56 -0
- package/dist/event-log.js +214 -0
- package/dist/llm.d.ts +29 -0
- package/dist/llm.js +155 -0
- package/dist/logger.d.ts +94 -0
- package/dist/logger.js +287 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +54 -0
- package/dist/memory/confidence.d.ts +7 -0
- package/dist/memory/confidence.js +37 -0
- package/dist/memory/consolidation.d.ts +26 -0
- package/dist/memory/consolidation.js +166 -0
- package/dist/memory/db.d.ts +40 -0
- package/dist/memory/db.js +283 -0
- package/dist/memory/dedup.d.ts +6 -0
- package/dist/memory/dedup.js +50 -0
- package/dist/memory/embedder-factory.d.ts +3 -0
- package/dist/memory/embedder-factory.js +81 -0
- package/dist/memory/embeddings.d.ts +15 -0
- package/dist/memory/embeddings.js +67 -0
- package/dist/memory/index.d.ts +9 -0
- package/dist/memory/index.js +11 -0
- package/dist/memory/ollama-summarizer.d.ts +19 -0
- package/dist/memory/ollama-summarizer.js +72 -0
- package/dist/memory/openai-summarizer.d.ts +21 -0
- package/dist/memory/openai-summarizer.js +51 -0
- package/dist/memory/store.d.ts +61 -0
- package/dist/memory/store.js +502 -0
- package/dist/memory/summarizer-factory.d.ts +3 -0
- package/dist/memory/summarizer-factory.js +69 -0
- package/dist/memory/summarizer.d.ts +4 -0
- package/dist/memory/summarizer.js +112 -0
- package/dist/memory/types.d.ts +87 -0
- package/dist/memory/types.js +17 -0
- package/dist/model-context.d.ts +15 -0
- package/dist/model-context.js +212 -0
- package/dist/project-detector.d.ts +37 -0
- package/dist/project-detector.js +604 -0
- package/dist/render.d.ts +15 -0
- package/dist/render.js +46 -0
- package/dist/session.d.ts +118 -0
- package/dist/session.js +809 -0
- package/dist/skills/index.d.ts +69 -0
- package/dist/skills/index.js +885 -0
- package/dist/skills/types.d.ts +93 -0
- package/dist/skills/types.js +8 -0
- package/dist/slash-commands.d.ts +14 -0
- package/dist/slash-commands.js +301 -0
- package/dist/state-graph.d.ts +38 -0
- package/dist/state-graph.js +255 -0
- package/dist/status-bar.d.ts +54 -0
- package/dist/status-bar.js +184 -0
- package/dist/thinking-display.d.ts +21 -0
- package/dist/thinking-display.js +37 -0
- package/dist/tool-summary.d.ts +4 -0
- package/dist/tool-summary.js +67 -0
- package/dist/tools/index.d.ts +925 -0
- package/dist/tools/index.js +86 -0
- package/dist/tools/knowledge.d.ts +140 -0
- package/dist/tools/knowledge.js +260 -0
- package/dist/tools/memory.d.ts +39 -0
- package/dist/tools/memory.js +300 -0
- package/dist/tools/search-code.d.ts +134 -0
- package/dist/tools/search-code.js +390 -0
- package/dist/tools/system.d.ts +16 -0
- package/dist/tools/system.js +499 -0
- package/dist/tools/tool-def.d.ts +6 -0
- package/dist/tools/tool-def.js +3 -0
- package/dist/turn-control.d.ts +51 -0
- package/dist/turn-control.js +210 -0
- package/dist/turn.d.ts +20 -0
- package/dist/turn.js +624 -0
- package/dist/types.d.ts +233 -0
- package/dist/types.js +4 -0
- package/dist/ui/readline-ui.d.ts +2 -0
- package/dist/ui/readline-ui.js +176 -0
- package/dist/ui/tui/app.d.ts +13 -0
- package/dist/ui/tui/app.js +270 -0
- package/dist/ui/tui/busy-indicator.d.ts +2 -0
- package/dist/ui/tui/busy-indicator.js +13 -0
- package/dist/ui/tui/components/gutter-rule.d.ts +5 -0
- package/dist/ui/tui/components/gutter-rule.js +9 -0
- package/dist/ui/tui/components/inline-tool-row.d.ts +10 -0
- package/dist/ui/tui/components/inline-tool-row.js +8 -0
- package/dist/ui/tui/components/prompt-input.d.ts +20 -0
- package/dist/ui/tui/components/prompt-input.js +120 -0
- package/dist/ui/tui/components/system-line.d.ts +5 -0
- package/dist/ui/tui/components/system-line.js +6 -0
- package/dist/ui/tui/components/thinking-block.d.ts +11 -0
- package/dist/ui/tui/components/thinking-block.js +31 -0
- package/dist/ui/tui/components/toast-line.d.ts +4 -0
- package/dist/ui/tui/components/toast-line.js +8 -0
- package/dist/ui/tui/components/tool-result-line.d.ts +5 -0
- package/dist/ui/tui/components/tool-result-line.js +6 -0
- package/dist/ui/tui/components/turn-footer.d.ts +5 -0
- package/dist/ui/tui/components/turn-footer.js +7 -0
- package/dist/ui/tui/components/user-block.d.ts +6 -0
- package/dist/ui/tui/components/user-block.js +6 -0
- package/dist/ui/tui/logo-banner.d.ts +5 -0
- package/dist/ui/tui/logo-banner.js +8 -0
- package/dist/ui/tui/markdown-render.d.ts +16 -0
- package/dist/ui/tui/markdown-render.js +218 -0
- package/dist/ui/tui/palette.d.ts +12 -0
- package/dist/ui/tui/palette.js +13 -0
- package/dist/ui/tui/reasoning-summary.d.ts +12 -0
- package/dist/ui/tui/reasoning-summary.js +27 -0
- package/dist/ui/tui/reducer.d.ts +92 -0
- package/dist/ui/tui/reducer.js +260 -0
- package/dist/ui/tui/run.d.ts +3 -0
- package/dist/ui/tui/run.js +40 -0
- package/dist/ui/tui/sink.d.ts +4 -0
- package/dist/ui/tui/sink.js +89 -0
- package/dist/ui/tui/status-bar-view.d.ts +5 -0
- package/dist/ui/tui/status-bar-view.js +44 -0
- package/dist/ui/tui/terminal-height.d.ts +12 -0
- package/dist/ui/tui/terminal-height.js +20 -0
- package/dist/ui/tui/terminal-width.d.ts +2 -0
- package/dist/ui/tui/terminal-width.js +5 -0
- package/dist/ui/tui/tool-display.d.ts +23 -0
- package/dist/ui/tui/tool-display.js +217 -0
- package/dist/ui/tui/transcript-line.d.ts +12 -0
- package/dist/ui/tui/transcript-line.js +43 -0
- package/dist/ui/tui/transcript-replay.d.ts +12 -0
- package/dist/ui/tui/transcript-replay.js +117 -0
- package/dist/ui-events.d.ts +39 -0
- package/dist/ui-events.js +33 -0
- package/dist/ui.d.ts +77 -0
- package/dist/ui.js +179 -0
- package/package.json +73 -0
- package/praana.config.example.toml +231 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Text, Box } from "ink";
|
|
4
|
+
import { marked } from "marked";
|
|
5
|
+
import { highlight as cliHighlight } from "cli-highlight";
|
|
6
|
+
import stripAnsi from "strip-ansi";
|
|
7
|
+
import { PALETTE } from "./palette.js";
|
|
8
|
+
import { getTerminalWidth } from "./terminal-width.js";
|
|
9
|
+
const HEADING_STYLES = {
|
|
10
|
+
1: { bold: true, color: PALETTE.assistant, underline: true },
|
|
11
|
+
2: { bold: true, color: PALETTE.assistant },
|
|
12
|
+
3: { bold: true, color: PALETTE.user },
|
|
13
|
+
4: { bold: true, color: PALETTE.muted },
|
|
14
|
+
5: { bold: true, color: PALETTE.muted },
|
|
15
|
+
6: { bold: true, color: PALETTE.muted },
|
|
16
|
+
};
|
|
17
|
+
function InlineToken({ token }) {
|
|
18
|
+
if (typeof token === "string") {
|
|
19
|
+
return _jsx(Text, { children: token });
|
|
20
|
+
}
|
|
21
|
+
switch (token.type) {
|
|
22
|
+
case "text": {
|
|
23
|
+
const t = token;
|
|
24
|
+
if (t.tokens && t.tokens.length > 0) {
|
|
25
|
+
return _jsx(InlineTokens, { tokens: t.tokens });
|
|
26
|
+
}
|
|
27
|
+
return _jsx(Text, { children: t.text });
|
|
28
|
+
}
|
|
29
|
+
case "strong": {
|
|
30
|
+
const t = token;
|
|
31
|
+
return (_jsx(Text, { bold: true, children: t.tokens?.length ? _jsx(InlineTokens, { tokens: t.tokens }) : t.text }));
|
|
32
|
+
}
|
|
33
|
+
case "em": {
|
|
34
|
+
const t = token;
|
|
35
|
+
return (_jsx(Text, { italic: true, children: t.tokens?.length ? _jsx(InlineTokens, { tokens: t.tokens }) : t.text }));
|
|
36
|
+
}
|
|
37
|
+
case "del": {
|
|
38
|
+
const t = token;
|
|
39
|
+
return (_jsx(Text, { strikethrough: true, dimColor: true, children: t.tokens?.length ? _jsx(InlineTokens, { tokens: t.tokens }) : t.text }));
|
|
40
|
+
}
|
|
41
|
+
case "codespan":
|
|
42
|
+
return (_jsxs(Text, { color: PALETTE.tool, backgroundColor: "#2d2d2d", children: [" ", token.text, " "] }));
|
|
43
|
+
case "link": {
|
|
44
|
+
const t = token;
|
|
45
|
+
const label = t.text || t.href;
|
|
46
|
+
const showHref = t.href && t.href !== t.text;
|
|
47
|
+
return (_jsxs(Text, { color: PALETTE.assistant, underline: true, children: [label, showHref ? (_jsx(Text, { dimColor: true, children: ` (${t.href})` })) : null] }));
|
|
48
|
+
}
|
|
49
|
+
case "br":
|
|
50
|
+
return _jsx(Text, { children: "\n" });
|
|
51
|
+
case "escape":
|
|
52
|
+
return _jsx(Text, { children: token.text });
|
|
53
|
+
default:
|
|
54
|
+
return _jsx(Text, { children: token.raw ?? "" });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function InlineTokens({ tokens }) {
|
|
58
|
+
return (_jsx(_Fragment, { children: tokens.map((token, i) => (_jsx(InlineToken, { token: token }, i))) }));
|
|
59
|
+
}
|
|
60
|
+
function MarkdownList({ token, depth = 0, }) {
|
|
61
|
+
return (_jsx(Box, { flexDirection: "column", marginBottom: depth === 0 ? 1 : 0, paddingLeft: depth > 0 ? 2 : 2, children: token.items.map((item, i) => {
|
|
62
|
+
const bullet = token.ordered ? `${i + 1}. ` : "• ";
|
|
63
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: PALETTE.gutter, children: bullet }), _jsx(Text, { wrap: "wrap", children: _jsx(ListItemTokens, { tokens: item.tokens, depth: depth }) })] }, i));
|
|
64
|
+
}) }));
|
|
65
|
+
}
|
|
66
|
+
function ListItemTokens({ tokens, depth = 0, }) {
|
|
67
|
+
return (_jsx(_Fragment, { children: tokens.map((token, i) => {
|
|
68
|
+
if (token.type === "space") {
|
|
69
|
+
return _jsx(Text, { children: "\n" }, i);
|
|
70
|
+
}
|
|
71
|
+
if (token.type === "paragraph") {
|
|
72
|
+
const p = token;
|
|
73
|
+
return (_jsx(Text, { wrap: "wrap", children: _jsx(InlineTokens, { tokens: p.tokens }) }, i));
|
|
74
|
+
}
|
|
75
|
+
if (token.type === "list") {
|
|
76
|
+
return (_jsx(MarkdownList, { token: token, depth: depth + 1 }, i));
|
|
77
|
+
}
|
|
78
|
+
return _jsx(InlineToken, { token: token }, i);
|
|
79
|
+
}) }));
|
|
80
|
+
}
|
|
81
|
+
/** Flatten inline tokens to plain text (used in tests). */
|
|
82
|
+
export function plainTextFromInlineTokens(tokens) {
|
|
83
|
+
return tokens
|
|
84
|
+
.map((token) => {
|
|
85
|
+
if (typeof token === "string")
|
|
86
|
+
return token;
|
|
87
|
+
switch (token.type) {
|
|
88
|
+
case "text": {
|
|
89
|
+
const t = token;
|
|
90
|
+
if (t.tokens?.length)
|
|
91
|
+
return plainTextFromInlineTokens(t.tokens);
|
|
92
|
+
return t.text ?? t.raw ?? "";
|
|
93
|
+
}
|
|
94
|
+
case "strong":
|
|
95
|
+
return token.text;
|
|
96
|
+
case "em":
|
|
97
|
+
return token.text;
|
|
98
|
+
case "del": {
|
|
99
|
+
const t = token;
|
|
100
|
+
if (t.tokens?.length)
|
|
101
|
+
return plainTextFromInlineTokens(t.tokens);
|
|
102
|
+
return t.text ?? "";
|
|
103
|
+
}
|
|
104
|
+
case "codespan":
|
|
105
|
+
return token.text;
|
|
106
|
+
case "link":
|
|
107
|
+
return token.text;
|
|
108
|
+
case "br":
|
|
109
|
+
return "\n";
|
|
110
|
+
case "escape":
|
|
111
|
+
return token.text;
|
|
112
|
+
default:
|
|
113
|
+
return token.raw ?? "";
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
.join("");
|
|
117
|
+
}
|
|
118
|
+
export function extractCellText(cell) {
|
|
119
|
+
if (cell.tokens && cell.tokens.length > 0) {
|
|
120
|
+
return plainTextFromInlineTokens(cell.tokens);
|
|
121
|
+
}
|
|
122
|
+
return cell.text ?? "";
|
|
123
|
+
}
|
|
124
|
+
function truncatePlain(text, width) {
|
|
125
|
+
if (text.length <= width)
|
|
126
|
+
return text;
|
|
127
|
+
return width <= 1 ? "…" : `${text.slice(0, width - 1)}…`;
|
|
128
|
+
}
|
|
129
|
+
function TableCellContent({ cell, width, bold, }) {
|
|
130
|
+
const plain = extractCellText(cell);
|
|
131
|
+
const truncated = plain.length > width;
|
|
132
|
+
if (truncated) {
|
|
133
|
+
return (_jsx(Text, { bold: bold, wrap: "truncate", children: truncatePlain(plain, width) }));
|
|
134
|
+
}
|
|
135
|
+
const pad = " ".repeat(Math.max(0, width - plain.length));
|
|
136
|
+
return (_jsxs(Text, { bold: bold, children: [_jsx(InlineTokens, { tokens: cell.tokens }), pad] }));
|
|
137
|
+
}
|
|
138
|
+
function TableRow({ cells, colWidths, header, }) {
|
|
139
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: header ? PALETTE.assistant : undefined, bold: header, children: "\u2502" }), cells.map((cell, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { children: " " }), _jsx(TableCellContent, { cell: cell, width: colWidths[i], bold: header }), _jsx(Text, { children: " " }), _jsx(Text, { color: PALETTE.gutter, children: "\u2502" })] }, i)))] }));
|
|
140
|
+
}
|
|
141
|
+
export function computeColWidths(headerTexts, bodyTexts, terminalWidth = getTerminalWidth()) {
|
|
142
|
+
const colCount = headerTexts.length;
|
|
143
|
+
const gutter = 2 + colCount * 3;
|
|
144
|
+
const perColMax = Math.max(8, Math.min(48, Math.floor((terminalWidth - gutter) / Math.max(colCount, 1))));
|
|
145
|
+
return Array.from({ length: colCount }, (_, ci) => {
|
|
146
|
+
const headerLen = stripAnsi(headerTexts[ci] ?? "").length;
|
|
147
|
+
const maxBodyLen = bodyTexts.reduce((max, row) => Math.max(max, stripAnsi(row[ci] ?? "").length), 0);
|
|
148
|
+
return Math.min(Math.max(headerLen, maxBodyLen, 4), perColMax);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function renderCodeBlock(token, syntaxHighlighting, syntaxTheme) {
|
|
152
|
+
const code = token.text.replace(/\n$/, "");
|
|
153
|
+
let highlighted;
|
|
154
|
+
if (syntaxHighlighting && token.lang) {
|
|
155
|
+
try {
|
|
156
|
+
highlighted = cliHighlight(code, { language: token.lang, theme: syntaxTheme, ignoreIllegals: true });
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
highlighted = code;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
highlighted = code;
|
|
164
|
+
}
|
|
165
|
+
const lines = highlighted.split("\n");
|
|
166
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [token.lang && (_jsxs(Text, { color: PALETTE.muted, dimColor: true, children: [" ", token.lang] })), _jsx(Box, { flexDirection: "column", backgroundColor: "#1e1e1e", paddingX: 1, children: lines.map((line, i) => (_jsx(Text, { children: line || " " }, i))) })] }));
|
|
167
|
+
}
|
|
168
|
+
function MarkdownBlock({ token, syntaxHighlighting, syntaxTheme }) {
|
|
169
|
+
switch (token.type) {
|
|
170
|
+
case "heading": {
|
|
171
|
+
const t = token;
|
|
172
|
+
const style = HEADING_STYLES[t.depth] ?? HEADING_STYLES[3];
|
|
173
|
+
return (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: style.bold, color: style.color, underline: style.underline, children: _jsx(InlineTokens, { tokens: t.tokens }) }) }));
|
|
174
|
+
}
|
|
175
|
+
case "paragraph": {
|
|
176
|
+
const t = token;
|
|
177
|
+
return (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { wrap: "wrap", children: _jsx(InlineTokens, { tokens: t.tokens }) }) }));
|
|
178
|
+
}
|
|
179
|
+
case "code": {
|
|
180
|
+
const t = token;
|
|
181
|
+
return renderCodeBlock(t, syntaxHighlighting, syntaxTheme);
|
|
182
|
+
}
|
|
183
|
+
case "list":
|
|
184
|
+
return _jsx(MarkdownList, { token: token });
|
|
185
|
+
case "table": {
|
|
186
|
+
const t = token;
|
|
187
|
+
const headerTexts = t.header.map(extractCellText);
|
|
188
|
+
const bodyTexts = t.rows.map((row) => row.map(extractCellText));
|
|
189
|
+
const colWidths = computeColWidths(headerTexts, bodyTexts);
|
|
190
|
+
const separator = colWidths.map((w) => "─".repeat(w));
|
|
191
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(TableRow, { cells: t.header, colWidths: colWidths, header: true }), _jsxs(Text, { color: PALETTE.gutter, children: ["\u251C\u2500", separator.join("─┼─"), "\u2500\u2524"] }), t.rows.map((row, i) => (_jsx(TableRow, { cells: row, colWidths: colWidths }, i)))] }));
|
|
192
|
+
}
|
|
193
|
+
case "blockquote": {
|
|
194
|
+
const t = token;
|
|
195
|
+
return (_jsx(Box, { flexDirection: "column", marginBottom: 1, paddingLeft: 2, children: t.tokens.map((tk, i) => {
|
|
196
|
+
if (tk.type === "paragraph") {
|
|
197
|
+
const p = tk;
|
|
198
|
+
return (_jsx(Text, { wrap: "wrap", color: PALETTE.muted, italic: true, children: _jsx(InlineTokens, { tokens: p.tokens }) }, i));
|
|
199
|
+
}
|
|
200
|
+
return (_jsx(Text, { color: PALETTE.muted, children: typeof tk === "string" ? tk : tk.raw ?? "" }, i));
|
|
201
|
+
}) }));
|
|
202
|
+
}
|
|
203
|
+
case "hr":
|
|
204
|
+
return (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: PALETTE.gutter, children: "─".repeat(Math.max(20, getTerminalWidth() - 4)) }) }));
|
|
205
|
+
case "html":
|
|
206
|
+
return _jsx(Text, { children: token.raw });
|
|
207
|
+
case "space":
|
|
208
|
+
return null;
|
|
209
|
+
default:
|
|
210
|
+
return _jsx(Text, { children: token.raw ?? "" });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
export const MarkdownRender = React.memo(function MarkdownRender({ text, syntaxHighlighting = true, syntaxTheme = "solarized-dark", }) {
|
|
214
|
+
if (!text)
|
|
215
|
+
return _jsx(Text, { children: " " });
|
|
216
|
+
const tokens = marked.lexer(text);
|
|
217
|
+
return (_jsx(Box, { flexDirection: "column", children: tokens.map((token, i) => (_jsx(MarkdownBlock, { token: token, syntaxHighlighting: syntaxHighlighting, syntaxTheme: syntaxTheme }, i))) }));
|
|
218
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* Colour palette for the ARIA TUI */
|
|
2
|
+
export const PALETTE = {
|
|
3
|
+
user: "#B7E4C7", // green-600
|
|
4
|
+
assistant: "#fbbf24", // amber-400
|
|
5
|
+
thinking: "#6b7280", // gray-500
|
|
6
|
+
tool: "#f59e0b", // amber-500
|
|
7
|
+
system: "#6b7280", // gray-500
|
|
8
|
+
border: "#fbbf24", // amber-400 for input bar
|
|
9
|
+
gutter: "#374151", // gray-700 for turn-group gutter
|
|
10
|
+
muted: "#475569", // gray-600
|
|
11
|
+
error: "#ef4444", // red-500
|
|
12
|
+
text: "#FAF9F6"
|
|
13
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Extract a disclosure title from reasoning text (OpenAI-style **Title** blocks). */
|
|
2
|
+
export declare function reasoningSummary(text: string): {
|
|
3
|
+
title: string | null;
|
|
4
|
+
body: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function formatThinkingDuration(ms: number): string;
|
|
7
|
+
export declare const THINKING_PREVIEW_LINES = 3;
|
|
8
|
+
/** Truncate thinking body for collapsed preview. */
|
|
9
|
+
export declare function truncateThinkingBody(body: string, maxLines?: number): {
|
|
10
|
+
text: string;
|
|
11
|
+
truncated: boolean;
|
|
12
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Extract a disclosure title from reasoning text (OpenAI-style **Title** blocks). */
|
|
2
|
+
export function reasoningSummary(text) {
|
|
3
|
+
const content = text.trim();
|
|
4
|
+
const match = content.match(/^\*\*([^*\n]+)\*\*(?:\r?\n\r?\n|$)/);
|
|
5
|
+
if (!match)
|
|
6
|
+
return { title: null, body: content };
|
|
7
|
+
return { title: match[1].trim(), body: content.slice(match[0].length).trimEnd() };
|
|
8
|
+
}
|
|
9
|
+
export function formatThinkingDuration(ms) {
|
|
10
|
+
if (ms < 1000)
|
|
11
|
+
return `${Math.max(0, Math.round(ms))}ms`;
|
|
12
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
13
|
+
}
|
|
14
|
+
export const THINKING_PREVIEW_LINES = 3;
|
|
15
|
+
/** Truncate thinking body for collapsed preview. */
|
|
16
|
+
export function truncateThinkingBody(body, maxLines = THINKING_PREVIEW_LINES) {
|
|
17
|
+
const trimmed = body.trim();
|
|
18
|
+
if (!trimmed)
|
|
19
|
+
return { text: "", truncated: false };
|
|
20
|
+
const lines = trimmed.split("\n");
|
|
21
|
+
if (lines.length <= maxLines)
|
|
22
|
+
return { text: trimmed, truncated: false };
|
|
23
|
+
return {
|
|
24
|
+
text: lines.slice(0, maxLines).join("\n"),
|
|
25
|
+
truncated: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { MemoryBannerStats } from "../../ui-events.js";
|
|
2
|
+
export type TranscriptRole = "user" | "assistant" | "system" | "tool" | "thinking" | "tool_result" | "turn_footer";
|
|
3
|
+
export interface TranscriptEntry {
|
|
4
|
+
id: string;
|
|
5
|
+
role: TranscriptRole;
|
|
6
|
+
text: string;
|
|
7
|
+
toolName?: string;
|
|
8
|
+
/** Compact result summary attached to a tool entry */
|
|
9
|
+
resultSummary?: string;
|
|
10
|
+
isError?: boolean;
|
|
11
|
+
/** Raw result text for error detail */
|
|
12
|
+
resultText?: string;
|
|
13
|
+
/** Display icon for tool rows */
|
|
14
|
+
toolIcon?: string;
|
|
15
|
+
/** Human-readable tool label */
|
|
16
|
+
toolLabel?: string;
|
|
17
|
+
/** Pending verb while tool is running */
|
|
18
|
+
toolPending?: string;
|
|
19
|
+
/** Group ID for visual turn grouping — increments on each user message */
|
|
20
|
+
group: number;
|
|
21
|
+
/** Epoch ms when live thinking started */
|
|
22
|
+
thinkingStartedAt?: number;
|
|
23
|
+
/** Elapsed thinking time once completed */
|
|
24
|
+
durationMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface TranscriptState {
|
|
27
|
+
completed: TranscriptEntry[];
|
|
28
|
+
live: TranscriptEntry | null;
|
|
29
|
+
nextId: number;
|
|
30
|
+
liveKind: "assistant" | "thinking" | null;
|
|
31
|
+
busy: boolean;
|
|
32
|
+
groupCounter: number;
|
|
33
|
+
}
|
|
34
|
+
export type TranscriptAction = {
|
|
35
|
+
type: "set_busy";
|
|
36
|
+
busy: boolean;
|
|
37
|
+
} | {
|
|
38
|
+
type: "user_message";
|
|
39
|
+
text: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: "assistant_delta";
|
|
42
|
+
delta: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: "assistant_complete";
|
|
45
|
+
} | {
|
|
46
|
+
type: "thinking_delta";
|
|
47
|
+
delta: string;
|
|
48
|
+
} | {
|
|
49
|
+
type: "thinking_close";
|
|
50
|
+
} | {
|
|
51
|
+
type: "tool_call";
|
|
52
|
+
toolName: string;
|
|
53
|
+
args: Record<string, unknown>;
|
|
54
|
+
} | {
|
|
55
|
+
type: "tool_result";
|
|
56
|
+
toolName: string;
|
|
57
|
+
resultText: string;
|
|
58
|
+
isError?: boolean;
|
|
59
|
+
} | {
|
|
60
|
+
type: "turn_footer";
|
|
61
|
+
model: string;
|
|
62
|
+
durationMs: number;
|
|
63
|
+
stats?: MemoryBannerStats;
|
|
64
|
+
} | {
|
|
65
|
+
type: "system_lines";
|
|
66
|
+
lines: string[];
|
|
67
|
+
} | {
|
|
68
|
+
type: "memory_banner";
|
|
69
|
+
text: string;
|
|
70
|
+
} | {
|
|
71
|
+
type: "interrupted";
|
|
72
|
+
} | {
|
|
73
|
+
type: "error";
|
|
74
|
+
message: string;
|
|
75
|
+
} | {
|
|
76
|
+
type: "bootstrap";
|
|
77
|
+
entries: TranscriptEntry[];
|
|
78
|
+
};
|
|
79
|
+
export declare function createInitialTranscriptState(): TranscriptState;
|
|
80
|
+
/** Drop brief pre-tool narration ("Let me check…") that clutters tool loops. */
|
|
81
|
+
export declare const SHORT_PRE_TOOL_NARRATION_MAX = 100;
|
|
82
|
+
export declare function transcriptReducer(state: TranscriptState, action: TranscriptAction): TranscriptState;
|
|
83
|
+
export declare function formatMemoryBannerLine(stats: {
|
|
84
|
+
activeState: number;
|
|
85
|
+
totalState: number;
|
|
86
|
+
digestLen: number;
|
|
87
|
+
recallCalls: number;
|
|
88
|
+
recallHits: number;
|
|
89
|
+
autoHydrated: number;
|
|
90
|
+
promptTokens: number;
|
|
91
|
+
outputTokens: number;
|
|
92
|
+
}): string;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { summarizeArgs } from "../../tool-summary.js";
|
|
2
|
+
import { formatToolDisplay, formatTurnFooter, summarizeResultForDisplay, } from "./tool-display.js";
|
|
3
|
+
export function createInitialTranscriptState() {
|
|
4
|
+
return {
|
|
5
|
+
completed: [],
|
|
6
|
+
live: null,
|
|
7
|
+
nextId: 1,
|
|
8
|
+
liveKind: null,
|
|
9
|
+
busy: false,
|
|
10
|
+
groupCounter: 0,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function nextId(state) {
|
|
14
|
+
const id = `m-${state.nextId}`;
|
|
15
|
+
return [id, { ...state, nextId: state.nextId + 1 }];
|
|
16
|
+
}
|
|
17
|
+
function pushCompleted(state, entry) {
|
|
18
|
+
const [id, s] = nextId(state);
|
|
19
|
+
return {
|
|
20
|
+
...s,
|
|
21
|
+
completed: [...s.completed, { ...entry, id, group: state.groupCounter }],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function freezeLive(state) {
|
|
25
|
+
if (!state.live)
|
|
26
|
+
return state;
|
|
27
|
+
return {
|
|
28
|
+
...state,
|
|
29
|
+
completed: [...state.completed, state.live],
|
|
30
|
+
live: null,
|
|
31
|
+
liveKind: null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Drop a live assistant entry without pushing empty rows into completed. */
|
|
35
|
+
function clearEmptyAssistantLive(state) {
|
|
36
|
+
if (state.liveKind !== "assistant" || !state.live || state.live.text.trim()) {
|
|
37
|
+
return state;
|
|
38
|
+
}
|
|
39
|
+
return { ...state, live: null, liveKind: null };
|
|
40
|
+
}
|
|
41
|
+
/** Drop brief pre-tool narration ("Let me check…") that clutters tool loops. */
|
|
42
|
+
export const SHORT_PRE_TOOL_NARRATION_MAX = 100;
|
|
43
|
+
function clearShortAssistantLive(state) {
|
|
44
|
+
if (state.liveKind !== "assistant" || !state.live)
|
|
45
|
+
return state;
|
|
46
|
+
const text = state.live.text.trim();
|
|
47
|
+
if (!text || text.length >= SHORT_PRE_TOOL_NARRATION_MAX)
|
|
48
|
+
return state;
|
|
49
|
+
return { ...state, live: null, liveKind: null };
|
|
50
|
+
}
|
|
51
|
+
function ensureAssistantPlaceholder(state) {
|
|
52
|
+
if (state.live)
|
|
53
|
+
return state;
|
|
54
|
+
const [id, s] = nextId(state);
|
|
55
|
+
return {
|
|
56
|
+
...s,
|
|
57
|
+
liveKind: "assistant",
|
|
58
|
+
live: { id, role: "assistant", text: "", group: state.groupCounter },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function transcriptReducer(state, action) {
|
|
62
|
+
switch (action.type) {
|
|
63
|
+
case "set_busy":
|
|
64
|
+
return { ...state, busy: action.busy };
|
|
65
|
+
case "bootstrap":
|
|
66
|
+
return {
|
|
67
|
+
...state,
|
|
68
|
+
completed: action.entries,
|
|
69
|
+
nextId: action.entries.length + 1,
|
|
70
|
+
};
|
|
71
|
+
case "user_message":
|
|
72
|
+
return pushCompleted({ ...state, groupCounter: state.groupCounter + 1 }, { role: "user", text: action.text });
|
|
73
|
+
case "assistant_delta": {
|
|
74
|
+
if (state.liveKind === "assistant" && state.live) {
|
|
75
|
+
return {
|
|
76
|
+
...state,
|
|
77
|
+
live: { ...state.live, text: state.live.text + action.delta },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const base = freezeLive(state);
|
|
81
|
+
const [id, s] = nextId(base);
|
|
82
|
+
return {
|
|
83
|
+
...s,
|
|
84
|
+
liveKind: "assistant",
|
|
85
|
+
live: { id, role: "assistant", text: action.delta, group: state.groupCounter },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
case "assistant_complete": {
|
|
89
|
+
// If there's no live entry or a thinking entry, no-op
|
|
90
|
+
if (!state.live || state.liveKind !== "assistant")
|
|
91
|
+
return state;
|
|
92
|
+
// If the live assistant entry has no text, silently drop it
|
|
93
|
+
if (!state.live.text.trim()) {
|
|
94
|
+
return { ...state, live: null, liveKind: null };
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
...state,
|
|
98
|
+
completed: [...state.completed, state.live],
|
|
99
|
+
live: null,
|
|
100
|
+
liveKind: null,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
case "thinking_delta": {
|
|
104
|
+
if (state.liveKind === "thinking" && state.live) {
|
|
105
|
+
return {
|
|
106
|
+
...state,
|
|
107
|
+
live: { ...state.live, text: state.live.text + action.delta },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
let base = state;
|
|
111
|
+
if (state.liveKind === "assistant" && state.live) {
|
|
112
|
+
base = state.live.text.trim() ? freezeLive(state) : clearEmptyAssistantLive(state);
|
|
113
|
+
}
|
|
114
|
+
const [id, s] = nextId(base);
|
|
115
|
+
return {
|
|
116
|
+
...s,
|
|
117
|
+
liveKind: "thinking",
|
|
118
|
+
live: {
|
|
119
|
+
id,
|
|
120
|
+
role: "thinking",
|
|
121
|
+
text: action.delta,
|
|
122
|
+
group: state.groupCounter,
|
|
123
|
+
thinkingStartedAt: Date.now(),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
case "thinking_close": {
|
|
128
|
+
if (state.liveKind !== "thinking" || !state.live)
|
|
129
|
+
return state;
|
|
130
|
+
const text = state.live.text.trim();
|
|
131
|
+
if (!text) {
|
|
132
|
+
return { ...state, live: null, liveKind: null };
|
|
133
|
+
}
|
|
134
|
+
const durationMs = state.live.thinkingStartedAt !== undefined
|
|
135
|
+
? Date.now() - state.live.thinkingStartedAt
|
|
136
|
+
: undefined;
|
|
137
|
+
const completedThinking = {
|
|
138
|
+
...state.live,
|
|
139
|
+
text: state.live.text,
|
|
140
|
+
durationMs,
|
|
141
|
+
};
|
|
142
|
+
// Freeze thinking to completed and immediately create an empty assistant
|
|
143
|
+
// placeholder so the live area is never null (prevents flash when tools start).
|
|
144
|
+
const frozen = {
|
|
145
|
+
...state,
|
|
146
|
+
completed: [...state.completed, completedThinking],
|
|
147
|
+
live: null,
|
|
148
|
+
liveKind: null,
|
|
149
|
+
};
|
|
150
|
+
const [id, s] = nextId(frozen);
|
|
151
|
+
return {
|
|
152
|
+
...s,
|
|
153
|
+
liveKind: "assistant",
|
|
154
|
+
live: { id, role: "assistant", text: "", group: state.groupCounter },
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
case "tool_call": {
|
|
158
|
+
const display = formatToolDisplay(action.toolName, action.args);
|
|
159
|
+
let base = clearShortAssistantLive(state);
|
|
160
|
+
base = ensureAssistantPlaceholder(base);
|
|
161
|
+
const summary = summarizeArgs(action.toolName, action.args);
|
|
162
|
+
return pushCompleted(base, {
|
|
163
|
+
role: "tool",
|
|
164
|
+
toolName: action.toolName,
|
|
165
|
+
text: summary ? `${action.toolName} :: ${summary}` : action.toolName,
|
|
166
|
+
toolIcon: display.icon,
|
|
167
|
+
toolLabel: display.label,
|
|
168
|
+
toolPending: display.pending,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
case "tool_result": {
|
|
172
|
+
const summary = summarizeResultForDisplay(action.resultText);
|
|
173
|
+
const completed = [...state.completed];
|
|
174
|
+
for (let i = completed.length - 1; i >= 0; i--) {
|
|
175
|
+
const entry = completed[i];
|
|
176
|
+
if (entry?.role === "tool" &&
|
|
177
|
+
entry.toolName === action.toolName &&
|
|
178
|
+
entry.resultSummary === undefined) {
|
|
179
|
+
completed[i] = {
|
|
180
|
+
...entry,
|
|
181
|
+
resultSummary: summary,
|
|
182
|
+
resultText: action.resultText,
|
|
183
|
+
isError: action.isError ?? false,
|
|
184
|
+
};
|
|
185
|
+
return { ...state, completed };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return pushCompleted(state, {
|
|
189
|
+
role: "tool_result",
|
|
190
|
+
toolName: action.toolName,
|
|
191
|
+
text: action.resultText,
|
|
192
|
+
resultSummary: summary,
|
|
193
|
+
resultText: action.resultText,
|
|
194
|
+
isError: action.isError ?? false,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
case "turn_footer":
|
|
198
|
+
return pushCompleted(state, {
|
|
199
|
+
role: "turn_footer",
|
|
200
|
+
text: formatTurnFooter(action.model, action.durationMs, action.stats),
|
|
201
|
+
});
|
|
202
|
+
case "system_lines": {
|
|
203
|
+
const text = action.lines.filter(Boolean).join("\n");
|
|
204
|
+
if (!text)
|
|
205
|
+
return state;
|
|
206
|
+
return pushCompleted(freezeLive(state), { role: "system", text });
|
|
207
|
+
}
|
|
208
|
+
case "memory_banner": {
|
|
209
|
+
if (!action.text)
|
|
210
|
+
return state;
|
|
211
|
+
return pushCompleted(state, { role: "system", text: action.text });
|
|
212
|
+
}
|
|
213
|
+
case "interrupted":
|
|
214
|
+
return pushCompleted(freezeLive(state), {
|
|
215
|
+
role: "system",
|
|
216
|
+
text: "[interrupted]",
|
|
217
|
+
});
|
|
218
|
+
case "error":
|
|
219
|
+
return pushCompleted(freezeLive(state), {
|
|
220
|
+
role: "system",
|
|
221
|
+
text: `[error] ${action.message}`,
|
|
222
|
+
});
|
|
223
|
+
default:
|
|
224
|
+
return state;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function fmtToken(n) {
|
|
228
|
+
if (n < 1000)
|
|
229
|
+
return String(n);
|
|
230
|
+
if (n < 1_000_000)
|
|
231
|
+
return `${(n / 1000).toFixed(1)}k`;
|
|
232
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
233
|
+
}
|
|
234
|
+
export function formatMemoryBannerLine(stats) {
|
|
235
|
+
if (stats.activeState === 0 &&
|
|
236
|
+
stats.recallCalls === 0 &&
|
|
237
|
+
stats.autoHydrated === 0 &&
|
|
238
|
+
stats.digestLen === 0 &&
|
|
239
|
+
!stats.promptTokens &&
|
|
240
|
+
!stats.outputTokens) {
|
|
241
|
+
return "";
|
|
242
|
+
}
|
|
243
|
+
const parts = [];
|
|
244
|
+
if (stats.activeState > 0 || stats.totalState > 0) {
|
|
245
|
+
parts.push(`state ${stats.activeState}/${stats.totalState}`);
|
|
246
|
+
}
|
|
247
|
+
if (stats.digestLen > 0)
|
|
248
|
+
parts.push(`digest ${stats.digestLen}c`);
|
|
249
|
+
if (stats.recallCalls > 0)
|
|
250
|
+
parts.push(`recall ${stats.recallHits}/${stats.recallCalls}`);
|
|
251
|
+
if (stats.autoHydrated > 0)
|
|
252
|
+
parts.push(`auto+${stats.autoHydrated}`);
|
|
253
|
+
if (stats.promptTokens > 0)
|
|
254
|
+
parts.push(`prompt ~${fmtToken(stats.promptTokens)}`);
|
|
255
|
+
if (stats.outputTokens > 0)
|
|
256
|
+
parts.push(`out ~${fmtToken(stats.outputTokens)}`);
|
|
257
|
+
if (parts.length === 0)
|
|
258
|
+
return "";
|
|
259
|
+
return parts.join(" · ");
|
|
260
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "ink";
|
|
3
|
+
import { formatSessionEndSummary, formatSessionEpilogue, } from "../../app-banner.js";
|
|
4
|
+
import { formatTuiBootSummary } from "./tool-display.js";
|
|
5
|
+
import { TuiApp } from "./app.js";
|
|
6
|
+
export async function runTui(controller, info, screen) {
|
|
7
|
+
const config = controller.config;
|
|
8
|
+
const session = controller.session;
|
|
9
|
+
const bootSummary = formatTuiBootSummary({
|
|
10
|
+
sessionId: session.id,
|
|
11
|
+
contextTokens: session.agentsContext
|
|
12
|
+
? Math.ceil(session.agentsContext.length / 4)
|
|
13
|
+
: undefined,
|
|
14
|
+
engineEnabled: session.isContextEngineEnabled(),
|
|
15
|
+
skillCount: session.skills.length,
|
|
16
|
+
memoryEnabled: session.memoryEnabled,
|
|
17
|
+
incognito: session.isIncognito(),
|
|
18
|
+
});
|
|
19
|
+
const { waitUntilExit, unmount } = render(React.createElement(TuiApp, {
|
|
20
|
+
controller,
|
|
21
|
+
initialStatus: controller.getStatusBarInput(),
|
|
22
|
+
recentLines: info.isResume ? [] : info.recentConversationLines,
|
|
23
|
+
transcriptBootstrap: info.transcriptBootstrap,
|
|
24
|
+
bootSummary,
|
|
25
|
+
markdownRendering: config.ui.markdown_rendering,
|
|
26
|
+
syntaxHighlighting: config.ui.syntax_highlighting,
|
|
27
|
+
syntaxTheme: config.ui.syntax_theme,
|
|
28
|
+
}), {
|
|
29
|
+
alternateScreen: screen === "alternate",
|
|
30
|
+
exitOnCtrlC: false,
|
|
31
|
+
patchConsole: false,
|
|
32
|
+
});
|
|
33
|
+
await waitUntilExit();
|
|
34
|
+
unmount();
|
|
35
|
+
await controller.shutdown();
|
|
36
|
+
for (const line of formatSessionEpilogue(controller.session.id)) {
|
|
37
|
+
console.log(line);
|
|
38
|
+
}
|
|
39
|
+
console.log(formatSessionEndSummary(controller.session));
|
|
40
|
+
}
|