aura-code 0.6.3 → 0.10.1
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/README.md +47 -164
- package/dist/agent/affect.d.ts +13 -0
- package/dist/agent/affect.js +34 -0
- package/dist/agent/affect.js.map +1 -0
- package/dist/agent/compactor.d.ts +27 -4
- package/dist/agent/compactor.js +185 -54
- package/dist/agent/compactor.js.map +1 -1
- package/dist/agent/confess.d.ts +35 -0
- package/dist/agent/confess.js +296 -0
- package/dist/agent/confess.js.map +1 -0
- package/dist/agent/context.d.ts +0 -4
- package/dist/agent/context.js +0 -38
- package/dist/agent/context.js.map +1 -1
- package/dist/agent/domain-expertise.d.ts +22 -0
- package/dist/agent/domain-expertise.js +110 -0
- package/dist/agent/domain-expertise.js.map +1 -0
- package/dist/agent/executive-queue.d.ts +26 -0
- package/dist/agent/executive-queue.js +50 -0
- package/dist/agent/executive-queue.js.map +1 -0
- package/dist/agent/generational-flush.d.ts +28 -0
- package/dist/agent/generational-flush.js +48 -0
- package/dist/agent/generational-flush.js.map +1 -0
- package/dist/agent/loop-profile.d.ts +50 -0
- package/dist/agent/loop-profile.js +88 -0
- package/dist/agent/loop-profile.js.map +1 -0
- package/dist/agent/loop.d.ts +14 -5
- package/dist/agent/loop.js +167 -39
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/memory-consolidate.d.ts +14 -0
- package/dist/agent/memory-consolidate.js +141 -0
- package/dist/agent/memory-consolidate.js.map +1 -0
- package/dist/agent/mixture.d.ts +33 -0
- package/dist/agent/mixture.js +138 -0
- package/dist/agent/mixture.js.map +1 -0
- package/dist/agent/spawner.js +2 -2
- package/dist/agent/spawner.js.map +1 -1
- package/dist/agent/system-prompt.d.ts +1 -1
- package/dist/agent/system-prompt.js +50 -22
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/unified-memory.d.ts +15 -0
- package/dist/agent/unified-memory.js +164 -0
- package/dist/agent/unified-memory.js.map +1 -0
- package/dist/checkpoints/engine.d.ts +44 -0
- package/dist/checkpoints/engine.js +324 -0
- package/dist/checkpoints/engine.js.map +1 -0
- package/dist/cli/command-palette.d.ts +26 -0
- package/dist/cli/command-palette.js +133 -0
- package/dist/cli/command-palette.js.map +1 -0
- package/dist/cli/context-health.d.ts +51 -0
- package/dist/cli/context-health.js +187 -0
- package/dist/cli/context-health.js.map +1 -0
- package/dist/cli/diamond.d.ts +67 -0
- package/dist/cli/diamond.js +222 -12
- package/dist/cli/diamond.js.map +1 -1
- package/dist/cli/dic.d.ts +2 -0
- package/dist/cli/dic.js +106 -0
- package/dist/cli/dic.js.map +1 -0
- package/dist/cli/diff-view.d.ts +18 -0
- package/dist/cli/diff-view.js +114 -0
- package/dist/cli/diff-view.js.map +1 -0
- package/dist/cli/display.d.ts +18 -4
- package/dist/cli/display.js +74 -151
- package/dist/cli/display.js.map +1 -1
- package/dist/cli/help-data.d.ts +7 -0
- package/dist/cli/help-data.js +80 -0
- package/dist/cli/help-data.js.map +1 -0
- package/dist/cli/index.js +1002 -852
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/markdown.d.ts +5 -0
- package/dist/cli/markdown.js +128 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/model-select.d.ts +39 -0
- package/dist/cli/model-select.js +65 -0
- package/dist/cli/model-select.js.map +1 -0
- package/dist/cli/tui.d.ts +34 -0
- package/dist/cli/tui.js +1176 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/config/defaults.d.ts +6 -17
- package/dist/config/defaults.js +25 -46
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/project-config.d.ts +2 -6
- package/dist/config/project-config.js +2 -2
- package/dist/config/project-config.js.map +1 -1
- package/dist/doctor/checks.d.ts +18 -0
- package/dist/doctor/checks.js +524 -0
- package/dist/doctor/checks.js.map +1 -0
- package/dist/doctor/index.d.ts +4 -0
- package/dist/doctor/index.js +229 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/doctor/repair.d.ts +19 -0
- package/dist/doctor/repair.js +126 -0
- package/dist/doctor/repair.js.map +1 -0
- package/dist/doctor/types.d.ts +38 -0
- package/dist/doctor/types.js +10 -0
- package/dist/doctor/types.js.map +1 -0
- package/dist/dream/dream.d.ts +79 -59
- package/dist/dream/dream.js +324 -144
- package/dist/dream/dream.js.map +1 -1
- package/dist/dream/episode.d.ts +17 -0
- package/dist/dream/episode.js +116 -0
- package/dist/dream/episode.js.map +1 -0
- package/dist/kanban/engine.d.ts +26 -0
- package/dist/kanban/engine.js +208 -0
- package/dist/kanban/engine.js.map +1 -0
- package/dist/kanban/types.d.ts +19 -0
- package/dist/kanban/types.js +9 -1
- package/dist/kanban/types.js.map +1 -1
- package/dist/machina/render-html.js +4 -2
- package/dist/machina/render-html.js.map +1 -1
- package/dist/machina/render-terminal.js +11 -4
- package/dist/machina/render-terminal.js.map +1 -1
- package/dist/machina/repair.d.ts +23 -0
- package/dist/machina/repair.js +98 -0
- package/dist/machina/repair.js.map +1 -0
- package/dist/machina/spec.js +12 -12
- package/dist/machina/spec.js.map +1 -1
- package/dist/machina/verify.d.ts +13 -1
- package/dist/machina/verify.js +12 -2
- package/dist/machina/verify.js.map +1 -1
- package/dist/mining/extract.d.ts +34 -0
- package/dist/mining/extract.js +229 -0
- package/dist/mining/extract.js.map +1 -0
- package/dist/mining/refine.d.ts +28 -0
- package/dist/mining/refine.js +256 -0
- package/dist/mining/refine.js.map +1 -0
- package/dist/perception/extractor.js +5 -3
- package/dist/perception/extractor.js.map +1 -1
- package/dist/perception/graph-store.d.ts +0 -11
- package/dist/perception/graph-store.js +0 -33
- package/dist/perception/graph-store.js.map +1 -1
- package/dist/perception/index.d.ts +1 -1
- package/dist/perception/index.js +1 -2
- package/dist/perception/index.js.map +1 -1
- package/dist/plugins/commands.d.ts +38 -0
- package/dist/plugins/commands.js +134 -0
- package/dist/plugins/commands.js.map +1 -0
- package/dist/plugins/frontmatter.d.ts +14 -0
- package/dist/plugins/frontmatter.js +78 -0
- package/dist/plugins/frontmatter.js.map +1 -0
- package/dist/plugins/hooks.d.ts +12 -0
- package/dist/plugins/hooks.js +148 -0
- package/dist/plugins/hooks.js.map +1 -0
- package/dist/plugins/loader.d.ts +7 -0
- package/dist/plugins/loader.js +255 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/market.d.ts +23 -0
- package/dist/plugins/market.js +281 -0
- package/dist/plugins/market.js.map +1 -0
- package/dist/plugins/types.d.ts +90 -0
- package/dist/plugins/types.js +20 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/providers/anthropic-oauth-draft.d.ts +119 -0
- package/dist/providers/anthropic-oauth-draft.js +414 -0
- package/dist/providers/anthropic-oauth-draft.js.map +1 -0
- package/dist/providers/anthropic.d.ts +4 -1
- package/dist/providers/anthropic.js +35 -7
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/factory.d.ts +43 -19
- package/dist/providers/factory.js +183 -129
- package/dist/providers/factory.js.map +1 -1
- package/dist/providers/google.js +9 -22
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/live-models.d.ts +23 -0
- package/dist/providers/live-models.js +145 -0
- package/dist/providers/live-models.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +1 -12
- package/dist/providers/openai-compatible.js +71 -108
- package/dist/providers/openai-compatible.js.map +1 -1
- package/dist/providers/resilient-factory.js +1 -1
- package/dist/providers/resilient-factory.js.map +1 -1
- package/dist/repl/queue.d.ts +22 -0
- package/dist/repl/queue.js +165 -0
- package/dist/repl/queue.js.map +1 -0
- package/dist/repl/side-channel.d.ts +23 -0
- package/dist/repl/side-channel.js +63 -0
- package/dist/repl/side-channel.js.map +1 -0
- package/dist/research/council.d.ts +2 -0
- package/dist/research/council.js +29 -4
- package/dist/research/council.js.map +1 -1
- package/dist/ruby/alternator.d.ts +11 -30
- package/dist/ruby/alternator.js +59 -38
- package/dist/ruby/alternator.js.map +1 -1
- package/dist/ruby/episode-capture.d.ts +0 -10
- package/dist/ruby/episode-capture.js +0 -33
- package/dist/ruby/episode-capture.js.map +1 -1
- package/dist/ruby/types.js +1 -1
- package/dist/ruby/types.js.map +1 -1
- package/dist/safety/path-jail.d.ts +17 -0
- package/dist/safety/path-jail.js +100 -0
- package/dist/safety/path-jail.js.map +1 -0
- package/dist/safety/permissions.d.ts +12 -21
- package/dist/safety/permissions.js +81 -216
- package/dist/safety/permissions.js.map +1 -1
- package/dist/safety/ssrf.d.ts +29 -0
- package/dist/safety/ssrf.js +185 -0
- package/dist/safety/ssrf.js.map +1 -0
- package/dist/server/index.js +64 -10
- package/dist/server/index.js.map +1 -1
- package/dist/setup/first-run.d.ts +0 -24
- package/dist/setup/first-run.js +10 -344
- package/dist/setup/first-run.js.map +1 -1
- package/dist/setup/key-store.d.ts +12 -0
- package/dist/setup/key-store.js +108 -0
- package/dist/setup/key-store.js.map +1 -0
- package/dist/setup/provider-registry.js +31 -18
- package/dist/setup/provider-registry.js.map +1 -1
- package/dist/setup/provider-test.d.ts +7 -0
- package/dist/setup/provider-test.js +73 -10
- package/dist/setup/provider-test.js.map +1 -1
- package/dist/setup/provider-wizard.d.ts +4 -1
- package/dist/setup/provider-wizard.js +92 -26
- package/dist/setup/provider-wizard.js.map +1 -1
- package/dist/setup/xiaomi.d.ts +1 -1
- package/dist/setup/xiaomi.js +3 -3
- package/dist/setup/xiaomi.js.map +1 -1
- package/dist/tools/browser.js +14 -10
- package/dist/tools/browser.js.map +1 -1
- package/dist/tools/clipboard.js +7 -1
- package/dist/tools/clipboard.js.map +1 -1
- package/dist/tools/dictate.d.ts +60 -0
- package/dist/tools/dictate.js +983 -0
- package/dist/tools/dictate.js.map +1 -0
- package/dist/tools/dictate_patched.js +1057 -0
- package/dist/tools/edit-file.js +10 -2
- package/dist/tools/edit-file.js.map +1 -1
- package/dist/tools/http-request.js +14 -4
- package/dist/tools/http-request.js.map +1 -1
- package/dist/tools/image-read.js +34 -2
- package/dist/tools/image-read.js.map +1 -1
- package/dist/tools/index.d.ts +16 -1
- package/dist/tools/index.js +59 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp.js +12 -0
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/read-file.js +10 -14
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/telegram-audio-policy.d.ts +30 -0
- package/dist/tools/telegram-audio-policy.js +49 -0
- package/dist/tools/telegram-audio-policy.js.map +1 -0
- package/dist/tools/telegram-bot.js +1136 -537
- package/dist/tools/telegram-bot.js.map +1 -1
- package/dist/tools/telegram-voice.d.ts +18 -5
- package/dist/tools/telegram-voice.js +54 -12
- package/dist/tools/telegram-voice.js.map +1 -1
- package/dist/tools/telegram.d.ts +3 -3
- package/dist/tools/telegram.js +5 -84
- package/dist/tools/telegram.js.map +1 -1
- package/dist/tools/tools.d.ts +2 -14
- package/dist/tools/tools.js +79 -144
- package/dist/tools/tools.js.map +1 -1
- package/dist/tools/web-fetch.js +39 -7
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts +0 -20
- package/dist/tools/web-search.js +40 -137
- package/dist/tools/web-search.js.map +1 -1
- package/dist/util/errors.js +1 -15
- package/dist/util/errors.js.map +1 -1
- package/dist/util/json-repair.d.ts +11 -0
- package/dist/util/json-repair.js +31 -0
- package/dist/util/json-repair.js.map +1 -0
- package/dist/util/rate-limiter.js +2 -8
- package/dist/util/rate-limiter.js.map +1 -1
- package/dist/viz/index.js +793 -792
- package/dist/viz/index.js.map +1 -1
- package/package.json +12 -32
package/dist/cli/tui.js
ADDED
|
@@ -0,0 +1,1176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.inputActive = void 0;
|
|
7
|
+
exports.setAgentMode = setAgentMode;
|
|
8
|
+
exports.getAgentMode = getAgentMode;
|
|
9
|
+
exports.setModeChangeHandler = setModeChangeHandler;
|
|
10
|
+
exports.setFooter = setFooter;
|
|
11
|
+
exports.setPanelContent = setPanelContent;
|
|
12
|
+
exports.setStatusLine = setStatusLine;
|
|
13
|
+
exports.createAbortController = createAbortController;
|
|
14
|
+
exports.clearAbortController = clearAbortController;
|
|
15
|
+
exports.setSessionSwitchHandler = setSessionSwitchHandler;
|
|
16
|
+
exports.enterAltScreen = enterAltScreen;
|
|
17
|
+
exports.leaveAltScreen = leaveAltScreen;
|
|
18
|
+
exports.enterFullscreenPrompt = enterFullscreenPrompt;
|
|
19
|
+
exports.exitFullscreenPrompt = exitFullscreenPrompt;
|
|
20
|
+
exports.writeOutput = writeOutput;
|
|
21
|
+
exports.writeStream = writeStream;
|
|
22
|
+
exports.setBannerLines = setBannerLines;
|
|
23
|
+
exports.askConfirm = askConfirm;
|
|
24
|
+
exports.askInput = askInput;
|
|
25
|
+
exports.startInput = startInput;
|
|
26
|
+
exports.stopInput = stopInput;
|
|
27
|
+
exports.setCallbacks = setCallbacks;
|
|
28
|
+
exports.setChatId = setChatId;
|
|
29
|
+
exports.initTui = initTui;
|
|
30
|
+
exports.destroyTui = destroyTui;
|
|
31
|
+
exports.createTuiDisplay = createTuiDisplay;
|
|
32
|
+
/**
|
|
33
|
+
* TUI v2 — Bottom-input layout: output scrolls at the top, input box
|
|
34
|
+
* pinned at the bottom. Inspired by OpenCode and Hermes Agent.
|
|
35
|
+
*
|
|
36
|
+
* Layout (using DECSTBM scroll region to protect bottom rows):
|
|
37
|
+
* ┌─────────────────────────────────────────────┐
|
|
38
|
+
* │ [banner] │ ← scroll region (top)
|
|
39
|
+
* │ [output scrollback — scrolls here] │
|
|
40
|
+
* │ ──────────────────────────────────────── │
|
|
41
|
+
* │ │ user message 6:42 PM │
|
|
42
|
+
* │ │ ◆ thinking… │
|
|
43
|
+
* │ │ ✓ done │
|
|
44
|
+
* ╞═════════════════════════════════════════════╡ ← scroll region boundary
|
|
45
|
+
* │ -- INSERT -- glm-5.2 · opencode-go │ ← fixed bottom (outside scroll)
|
|
46
|
+
* │ ╭ ask aura ──────────────────────────────╮ │
|
|
47
|
+
* │ │ your input here█ │ │
|
|
48
|
+
* │ ╰────────────────────────────────────────╯ │
|
|
49
|
+
* │ ◆ Context: ████░░░░ 40% · 2.1k/128k · $0.03 │
|
|
50
|
+
* └─────────────────────────────────────────────┘
|
|
51
|
+
*
|
|
52
|
+
* Key bindings:
|
|
53
|
+
* Ctrl+P — Command palette
|
|
54
|
+
* Ctrl+L — Session switcher
|
|
55
|
+
* Tab — Agent switcher (build/plan)
|
|
56
|
+
* Esc — Scroll mode (vim pager)
|
|
57
|
+
* Ctrl+C — Abort task or exit
|
|
58
|
+
*/
|
|
59
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
60
|
+
const context_health_js_1 = require("./context-health.js");
|
|
61
|
+
const diamond_js_1 = require("./diamond.js");
|
|
62
|
+
const command_palette_js_1 = require("./command-palette.js");
|
|
63
|
+
const markdown_js_1 = require("./markdown.js");
|
|
64
|
+
const TEXT = chalk_1.default.hex(diamond_js_1.TEXT_HEX);
|
|
65
|
+
const TEXT_DIM = chalk_1.default.hex(diamond_js_1.TEXT_DIM_HEX);
|
|
66
|
+
const ACCENT = chalk_1.default.hex('#cc785c');
|
|
67
|
+
const RUBY = diamond_js_1.RUBY_ACCENT;
|
|
68
|
+
// ── State ──────────────────────────────────────────────────────────────────
|
|
69
|
+
let inputBuffer = '';
|
|
70
|
+
let cursorPos = 0;
|
|
71
|
+
let inputActive = false;
|
|
72
|
+
exports.inputActive = inputActive;
|
|
73
|
+
let stdinHandler = null;
|
|
74
|
+
let chatId = '';
|
|
75
|
+
let agentMode = 'build';
|
|
76
|
+
let onModeChange = null;
|
|
77
|
+
function setAgentMode(mode) { agentMode = mode; if (onModeChange)
|
|
78
|
+
onModeChange(mode); }
|
|
79
|
+
function getAgentMode() { return agentMode; }
|
|
80
|
+
function setModeChangeHandler(fn) { onModeChange = fn; }
|
|
81
|
+
let overlay = 'none';
|
|
82
|
+
let overlayQuery = '';
|
|
83
|
+
let overlaySelected = 0;
|
|
84
|
+
function openOverlay(type) {
|
|
85
|
+
overlay = type;
|
|
86
|
+
overlayQuery = '';
|
|
87
|
+
overlaySelected = 0;
|
|
88
|
+
drawPromptBottom();
|
|
89
|
+
}
|
|
90
|
+
function closeOverlay() {
|
|
91
|
+
overlay = 'none';
|
|
92
|
+
overlayQuery = '';
|
|
93
|
+
overlaySelected = 0;
|
|
94
|
+
drawPromptBottom();
|
|
95
|
+
}
|
|
96
|
+
// ── Scroll mode ────────────────────────────────────────────────────────────
|
|
97
|
+
let scrollMode = false;
|
|
98
|
+
let scrollOffset = 0;
|
|
99
|
+
let pendingG = false;
|
|
100
|
+
let scrollBuffer = [];
|
|
101
|
+
const MAX_SCROLLBACK = 5000;
|
|
102
|
+
let streamAccum = '';
|
|
103
|
+
let bannerLines = [];
|
|
104
|
+
let activeTool = null;
|
|
105
|
+
function startToolSpinner(name) {
|
|
106
|
+
stopToolSpinner();
|
|
107
|
+
activeTool = { name, startTime: Date.now(), spinnerFrame: 0, intervalHandle: null };
|
|
108
|
+
if (!scrollMode) {
|
|
109
|
+
activeTool.intervalHandle = setInterval(() => {
|
|
110
|
+
if (!activeTool || scrollMode)
|
|
111
|
+
return;
|
|
112
|
+
activeTool.spinnerFrame++;
|
|
113
|
+
const spinners = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
114
|
+
const s = spinners[activeTool.spinnerFrame % spinners.length];
|
|
115
|
+
const elapsed = ((Date.now() - activeTool.startTime) / 1000).toFixed(1);
|
|
116
|
+
// Write spinner on the last output line (inside scroll region)
|
|
117
|
+
cursorCol(1);
|
|
118
|
+
clearEol();
|
|
119
|
+
rawWrite(ACCENT(` ${s} ${activeTool.name}`) + TEXT_DIM(` ${elapsed}s`));
|
|
120
|
+
cursorCol(1);
|
|
121
|
+
}, 100);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function stopToolSpinner() {
|
|
125
|
+
if (activeTool?.intervalHandle) {
|
|
126
|
+
clearInterval(activeTool.intervalHandle);
|
|
127
|
+
activeTool.intervalHandle = null;
|
|
128
|
+
}
|
|
129
|
+
if (activeTool && !scrollMode) {
|
|
130
|
+
cursorCol(1);
|
|
131
|
+
clearEol();
|
|
132
|
+
}
|
|
133
|
+
activeTool = null;
|
|
134
|
+
}
|
|
135
|
+
// ── Footer ──────────────────────────────────────────────────────────────────
|
|
136
|
+
let footerActive = false;
|
|
137
|
+
let footerText = '';
|
|
138
|
+
function setFooter(text) {
|
|
139
|
+
footerText = text;
|
|
140
|
+
footerActive = true;
|
|
141
|
+
if (inputActive)
|
|
142
|
+
drawPromptBottom();
|
|
143
|
+
}
|
|
144
|
+
let panelSuggestions = [];
|
|
145
|
+
function setPanelContent(opts) {
|
|
146
|
+
if (opts.suggestions)
|
|
147
|
+
panelSuggestions = opts.suggestions;
|
|
148
|
+
if (inputActive)
|
|
149
|
+
drawPromptBottom();
|
|
150
|
+
}
|
|
151
|
+
let statusLineText = '';
|
|
152
|
+
function setStatusLine(text) {
|
|
153
|
+
statusLineText = text;
|
|
154
|
+
if (inputActive)
|
|
155
|
+
drawPromptBottom();
|
|
156
|
+
}
|
|
157
|
+
function createAbortController() {
|
|
158
|
+
currentAbort = new AbortController();
|
|
159
|
+
return currentAbort;
|
|
160
|
+
}
|
|
161
|
+
function clearAbortController() { currentAbort = null; }
|
|
162
|
+
// ── Session switcher callback ───────────────────────────────────────────────
|
|
163
|
+
let onSessionSwitch = null;
|
|
164
|
+
function setSessionSwitchHandler(fn) { onSessionSwitch = fn; }
|
|
165
|
+
// ── Low-level ──────────────────────────────────────────────────────────────
|
|
166
|
+
let realStdoutWrite = null;
|
|
167
|
+
function rawWrite(s) {
|
|
168
|
+
(realStdoutWrite ?? process.stdout.write.bind(process.stdout))(s);
|
|
169
|
+
}
|
|
170
|
+
function clearEol() { rawWrite('\x1b[0K'); }
|
|
171
|
+
function cursorUp(n) { if (n > 0)
|
|
172
|
+
rawWrite(`\x1b[${n}A`); }
|
|
173
|
+
function cursorDown(n) { if (n > 0)
|
|
174
|
+
rawWrite(`\x1b[${n}B`); }
|
|
175
|
+
function cursorCol(n) { rawWrite(`\x1b[${n}G`); }
|
|
176
|
+
function hideCursor() { rawWrite('\x1b[?25l'); }
|
|
177
|
+
function showCursor() { rawWrite('\x1b[?25h'); }
|
|
178
|
+
let altScreenActive = false;
|
|
179
|
+
function enterAltScreen() {
|
|
180
|
+
altScreenActive = true;
|
|
181
|
+
rawWrite('\x1b[?1049h');
|
|
182
|
+
// OSC 11: set the terminal's default background to the palette's bluish
|
|
183
|
+
// dark. Restored via OSC 111 on leave — no per-line bg painting needed.
|
|
184
|
+
rawWrite(`\x1b]11;${diamond_js_1.BG_HEX}\x07`);
|
|
185
|
+
}
|
|
186
|
+
function leaveAltScreen() {
|
|
187
|
+
if (!altScreenActive)
|
|
188
|
+
return;
|
|
189
|
+
altScreenActive = false;
|
|
190
|
+
rawWrite('\x1b]111\x07'); // OSC 111: reset background to terminal default
|
|
191
|
+
rawWrite('\x1b[?1049l');
|
|
192
|
+
}
|
|
193
|
+
const cols = () => process.stdout.columns ?? 80;
|
|
194
|
+
const screenRows = () => process.stdout.rows ?? 24;
|
|
195
|
+
function handleExternalWrite(chunk) {
|
|
196
|
+
if (!inputActive) {
|
|
197
|
+
rawWrite(chunk);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (chunk.endsWith('\n')) {
|
|
201
|
+
writeOutput(chunk.slice(0, -1));
|
|
202
|
+
}
|
|
203
|
+
else if (scrollMode) {
|
|
204
|
+
streamAccum += chunk;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
rawWrite(chunk);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function patchStdout() {
|
|
211
|
+
if (realStdoutWrite)
|
|
212
|
+
return;
|
|
213
|
+
realStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
214
|
+
process.stdout.write = ((chunk) => {
|
|
215
|
+
handleExternalWrite(typeof chunk === 'string' ? chunk : String(chunk));
|
|
216
|
+
return true;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
function unpatchStdout() {
|
|
220
|
+
if (realStdoutWrite) {
|
|
221
|
+
process.stdout.write = realStdoutWrite;
|
|
222
|
+
realStdoutWrite = null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
process.on('exit', () => {
|
|
226
|
+
try {
|
|
227
|
+
rawWrite('\x1b[r'); // reset scroll region
|
|
228
|
+
leaveAltScreen();
|
|
229
|
+
unpatchStdout();
|
|
230
|
+
}
|
|
231
|
+
catch { /* best effort */ }
|
|
232
|
+
});
|
|
233
|
+
// ── Bottom-input layout via DECSTBM ─────────────────────────────────────────
|
|
234
|
+
//
|
|
235
|
+
// The screen is split into two regions using DECSTBM (scroll region):
|
|
236
|
+
// Rows 1..(sr-FIXED_BOTTOM) — scrollable output region
|
|
237
|
+
// Rows (sr-FIXED_BOTTOM+1)..sr — fixed bottom block (input box etc.)
|
|
238
|
+
//
|
|
239
|
+
// DECSTBM sets the region inside which scrolling/line-feed happens.
|
|
240
|
+
// The cursor jumps to the region's home when set — we account for that.
|
|
241
|
+
// The bottom block is redrawn in place whenever the input changes, by
|
|
242
|
+
// positioning the cursor to the bottom rows directly.
|
|
243
|
+
const BOX_CONTENT_ROWS = 3;
|
|
244
|
+
const HEADER_ROWS = 5; // top border + content + bottom border
|
|
245
|
+
const STATUS_ROW = 1;
|
|
246
|
+
const FOOTER_ROW = 1;
|
|
247
|
+
const FIXED_BOTTOM = HEADER_ROWS + STATUS_ROW + FOOTER_ROW; // 7
|
|
248
|
+
const LEAD = 2;
|
|
249
|
+
const MARGIN = 1;
|
|
250
|
+
function computeLayout() {
|
|
251
|
+
const total = cols();
|
|
252
|
+
return { boxWidth: Math.min(total - LEAD - MARGIN, 100) };
|
|
253
|
+
}
|
|
254
|
+
function wrapInput(innerWidth) {
|
|
255
|
+
const w = Math.max(4, innerWidth);
|
|
256
|
+
const chunks = [];
|
|
257
|
+
for (let i = 0; i < inputBuffer.length || i === 0; i += w) {
|
|
258
|
+
chunks.push(inputBuffer.slice(i, i + w));
|
|
259
|
+
if (inputBuffer.length === 0)
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
let cursorChunk = Math.floor(cursorPos / w);
|
|
263
|
+
let cursorColIdx = cursorPos % w;
|
|
264
|
+
let visible = chunks;
|
|
265
|
+
if (chunks.length > BOX_CONTENT_ROWS) {
|
|
266
|
+
const hidden = chunks.length - BOX_CONTENT_ROWS;
|
|
267
|
+
visible = chunks.slice(hidden);
|
|
268
|
+
cursorChunk -= hidden;
|
|
269
|
+
}
|
|
270
|
+
if (cursorChunk < 0) {
|
|
271
|
+
cursorChunk = 0;
|
|
272
|
+
cursorColIdx = 0;
|
|
273
|
+
}
|
|
274
|
+
while (visible.length < BOX_CONTENT_ROWS)
|
|
275
|
+
visible.push('');
|
|
276
|
+
return { rows: visible, cursorRow: cursorChunk, cursorCol: cursorColIdx };
|
|
277
|
+
}
|
|
278
|
+
function padVisible(s, width) {
|
|
279
|
+
const visibleLen = s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
280
|
+
return s + ' '.repeat(Math.max(0, width - visibleLen));
|
|
281
|
+
}
|
|
282
|
+
function truncVisible(s, width) {
|
|
283
|
+
const visibleLen = s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
284
|
+
if (visibleLen <= width)
|
|
285
|
+
return s;
|
|
286
|
+
let i = 0, visCount = 0, out = '';
|
|
287
|
+
while (i < s.length && visCount < Math.max(0, width - 1)) {
|
|
288
|
+
const m = s.slice(i).match(/^\x1b\[[0-9;]*m/);
|
|
289
|
+
if (m) {
|
|
290
|
+
out += m[0];
|
|
291
|
+
i += m[0].length;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
out += s[i];
|
|
295
|
+
visCount++;
|
|
296
|
+
i++;
|
|
297
|
+
}
|
|
298
|
+
return out + '\x1b[0m…';
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Build the bottom block lines: status line, input box, footer.
|
|
302
|
+
* Returns lines in top-to-bottom order.
|
|
303
|
+
*/
|
|
304
|
+
function buildBottomRows() {
|
|
305
|
+
const { boxWidth } = computeLayout();
|
|
306
|
+
const idTag = chatId ? ` ${chatId}` : '';
|
|
307
|
+
const label = ` ask aura${idTag} `;
|
|
308
|
+
const innerWidth = boxWidth - 4;
|
|
309
|
+
const wrapped = wrapInput(innerWidth);
|
|
310
|
+
const focused = !scrollMode;
|
|
311
|
+
const modeTag = focused ? ACCENT.bold('-- INSERT --') : ACCENT.bold('-- SCROLL --');
|
|
312
|
+
const agentTag = agentMode === 'plan' ? ACCENT(' [plan]') : '';
|
|
313
|
+
const cursorChar = focused ? ACCENT('█') : '';
|
|
314
|
+
const rows = [];
|
|
315
|
+
// Status line
|
|
316
|
+
rows.push(' '.repeat(LEAD) + padVisible(modeTag + agentTag + TEXT_DIM(` ${statusLineText}`), boxWidth));
|
|
317
|
+
// Input box
|
|
318
|
+
for (let row = 0; row < HEADER_ROWS; row++) {
|
|
319
|
+
let boxPart;
|
|
320
|
+
if (row === 0) {
|
|
321
|
+
const dashes = Math.max(0, boxWidth - label.length - 2);
|
|
322
|
+
boxPart = focused
|
|
323
|
+
? (0, diamond_js_1.gradient)('╭') + TEXT_DIM(label) + (0, diamond_js_1.gradient)('─'.repeat(dashes)) + (0, diamond_js_1.gradient)('╮')
|
|
324
|
+
: (0, diamond_js_1.CHROME_DIM)('╭' + label + '─'.repeat(dashes) + '╮');
|
|
325
|
+
}
|
|
326
|
+
else if (row === HEADER_ROWS - 1) {
|
|
327
|
+
boxPart = focused
|
|
328
|
+
? (0, diamond_js_1.gradient)('╰' + '─'.repeat(boxWidth - 2) + '╯')
|
|
329
|
+
: (0, diamond_js_1.CHROME_DIM)('╰' + '─'.repeat(boxWidth - 2) + '╯');
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
const contentRow = row - 1;
|
|
333
|
+
const text = wrapped.rows[contentRow] ?? '';
|
|
334
|
+
const isCursorRow = contentRow === wrapped.cursorRow;
|
|
335
|
+
const showPlaceholder = inputBuffer.length === 0 && contentRow === 0;
|
|
336
|
+
let inner;
|
|
337
|
+
if (showPlaceholder) {
|
|
338
|
+
inner = TEXT_DIM('type a task, :btw, :q, :help...') + cursorChar;
|
|
339
|
+
inner = padVisible(inner, innerWidth);
|
|
340
|
+
}
|
|
341
|
+
else if (isCursorRow && focused) {
|
|
342
|
+
const before = TEXT(text.slice(0, wrapped.cursorCol));
|
|
343
|
+
const after = TEXT(text.slice(wrapped.cursorCol));
|
|
344
|
+
inner = padVisible(before + cursorChar + after, innerWidth);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
inner = padVisible((focused ? TEXT : TEXT_DIM)(text), innerWidth);
|
|
348
|
+
}
|
|
349
|
+
const border = focused ? (0, diamond_js_1.gradientStopFor)(row, HEADER_ROWS)('│') : (0, diamond_js_1.CHROME_DIM)('│');
|
|
350
|
+
boxPart = border + ' ' + inner + ' ' + border;
|
|
351
|
+
}
|
|
352
|
+
rows.push(' '.repeat(LEAD) + boxPart);
|
|
353
|
+
}
|
|
354
|
+
return rows;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Set the terminal scroll region to exclude the bottom FIXED_BOTTOM rows.
|
|
358
|
+
* After DECSTBM, the cursor moves to the home position of the region.
|
|
359
|
+
*/
|
|
360
|
+
function setScrollRegion() {
|
|
361
|
+
const sr = screenRows();
|
|
362
|
+
const scrollEnd = sr - FIXED_BOTTOM;
|
|
363
|
+
if (scrollEnd < 1)
|
|
364
|
+
return;
|
|
365
|
+
// DECSTBM: \x1b[top;bottom;r — set scroll region from row `top` to `bottom`
|
|
366
|
+
rawWrite(`\x1b[1;${scrollEnd}r`);
|
|
367
|
+
// Move cursor to top of scroll region (DECSTBM homes cursor there)
|
|
368
|
+
rawWrite(`\x1b[1;1H`);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Reset scroll region to full screen.
|
|
372
|
+
*/
|
|
373
|
+
function resetScrollRegion() {
|
|
374
|
+
rawWrite('\x1b[r');
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Draw the bottom block (status + input box + footer) at the bottom of the screen.
|
|
378
|
+
* Uses absolute row positioning to the fixed bottom rows — these are OUTSIDE
|
|
379
|
+
* the scroll region, so they're never scrolled away.
|
|
380
|
+
*/
|
|
381
|
+
function drawPromptBottom() {
|
|
382
|
+
if (scrollMode || fullscreenPrompt)
|
|
383
|
+
return;
|
|
384
|
+
const rows = buildBottomRows();
|
|
385
|
+
const sr = screenRows();
|
|
386
|
+
// The bottom block starts at row (sr - FIXED_BOTTOM + 1)
|
|
387
|
+
const startRow = sr - FIXED_BOTTOM + 1;
|
|
388
|
+
for (let i = 0; i < rows.length; i++) {
|
|
389
|
+
const row = startRow + i;
|
|
390
|
+
rawWrite(`\x1b[${row};1H`);
|
|
391
|
+
clearEol();
|
|
392
|
+
rawWrite(rows[i]);
|
|
393
|
+
}
|
|
394
|
+
// Footer on the very last row
|
|
395
|
+
if (footerActive) {
|
|
396
|
+
rawWrite(`\x1b[${sr};1H`);
|
|
397
|
+
clearEol();
|
|
398
|
+
rawWrite(truncVisible(footerText, Math.max(10, cols() - MARGIN)));
|
|
399
|
+
}
|
|
400
|
+
// Return cursor to the scroll region (where output goes)
|
|
401
|
+
// Position at the bottom of the scroll region
|
|
402
|
+
const scrollEnd = sr - FIXED_BOTTOM;
|
|
403
|
+
rawWrite(`\x1b[${scrollEnd};1H`);
|
|
404
|
+
}
|
|
405
|
+
// ── Fullscreen mode for interactive prompts (wizard etc.) ──────────────────
|
|
406
|
+
//
|
|
407
|
+
// When an interactive prompt like the provider wizard needs many rows,
|
|
408
|
+
// collapse the bottom block and expand the scroll region to the full screen.
|
|
409
|
+
// The wizard's own readline handles input; the TUI's input box is hidden.
|
|
410
|
+
let fullscreenPrompt = false;
|
|
411
|
+
function enterFullscreenPrompt() {
|
|
412
|
+
fullscreenPrompt = true;
|
|
413
|
+
resetScrollRegion();
|
|
414
|
+
// Clear the bottom block area
|
|
415
|
+
const sr = screenRows();
|
|
416
|
+
for (let r = sr - FIXED_BOTTOM + 1; r <= sr; r++) {
|
|
417
|
+
rawWrite(`\x1b[${r};1H`);
|
|
418
|
+
clearEol();
|
|
419
|
+
}
|
|
420
|
+
// Move cursor to where output was
|
|
421
|
+
rawWrite(`\x1b[${sr - FIXED_BOTTOM};1H`);
|
|
422
|
+
}
|
|
423
|
+
function exitFullscreenPrompt() {
|
|
424
|
+
fullscreenPrompt = false;
|
|
425
|
+
setScrollRegion();
|
|
426
|
+
drawPromptBottom();
|
|
427
|
+
}
|
|
428
|
+
// ── Output ─────────────────────────────────────────────────────────────────
|
|
429
|
+
function wrapForTerminal(text) {
|
|
430
|
+
const maxWidth = Math.max(10, cols() - MARGIN);
|
|
431
|
+
const physicalLines = [];
|
|
432
|
+
for (const line of text.split('\n')) {
|
|
433
|
+
const visible = line.replace(/\x1b\[[0-9;]*m/g, '');
|
|
434
|
+
if (visible.length <= maxWidth) {
|
|
435
|
+
physicalLines.push(line);
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
let i = 0, visCount = 0, chunk = '';
|
|
439
|
+
while (i < line.length) {
|
|
440
|
+
const m = line.slice(i).match(/^\x1b\[[0-9;]*m/);
|
|
441
|
+
if (m) {
|
|
442
|
+
chunk += m[0];
|
|
443
|
+
i += m[0].length;
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (visCount >= maxWidth) {
|
|
447
|
+
physicalLines.push(chunk);
|
|
448
|
+
chunk = '';
|
|
449
|
+
visCount = 0;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
chunk += line[i];
|
|
453
|
+
visCount++;
|
|
454
|
+
i++;
|
|
455
|
+
}
|
|
456
|
+
physicalLines.push(chunk);
|
|
457
|
+
}
|
|
458
|
+
return { output: physicalLines.join('\n'), lineCount: physicalLines.length };
|
|
459
|
+
}
|
|
460
|
+
function writeOutput(text) {
|
|
461
|
+
if (streamAccum) {
|
|
462
|
+
pushScrollback(wrapForTerminal(streamAccum).output.split('\n'));
|
|
463
|
+
streamAccum = '';
|
|
464
|
+
}
|
|
465
|
+
const { output, lineCount } = wrapForTerminal(text);
|
|
466
|
+
pushScrollback(output.split('\n'));
|
|
467
|
+
if (scrollMode) {
|
|
468
|
+
scrollOffset = Math.min(scrollOffset + lineCount, maxScrollOffset());
|
|
469
|
+
renderScrollView();
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (fullscreenPrompt) {
|
|
473
|
+
// Fullscreen mode: just write naturally, no scroll region, no bottom block
|
|
474
|
+
rawWrite(output + '\n');
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// Write inside the scroll region — output scrolls naturally above the bottom block
|
|
478
|
+
cursorCol(1);
|
|
479
|
+
clearEol();
|
|
480
|
+
rawWrite(output);
|
|
481
|
+
rawWrite('\n');
|
|
482
|
+
}
|
|
483
|
+
function writeStream(text) {
|
|
484
|
+
streamAccum += text;
|
|
485
|
+
if (scrollMode)
|
|
486
|
+
return;
|
|
487
|
+
rawWrite(text);
|
|
488
|
+
}
|
|
489
|
+
function echoUserLine(line) {
|
|
490
|
+
const bar = RUBY('│ ');
|
|
491
|
+
writeOutput(bar + TEXT(line));
|
|
492
|
+
writeOutput(bar + TEXT_DIM(formatTime(new Date())));
|
|
493
|
+
}
|
|
494
|
+
function formatTime(d) {
|
|
495
|
+
let h = d.getHours();
|
|
496
|
+
const m = d.getMinutes().toString().padStart(2, '0');
|
|
497
|
+
const ampm = h >= 12 ? 'PM' : 'AM';
|
|
498
|
+
h = h % 12 || 12;
|
|
499
|
+
return `${h}:${m} ${ampm}`;
|
|
500
|
+
}
|
|
501
|
+
function pushScrollback(lines) {
|
|
502
|
+
for (const line of lines)
|
|
503
|
+
scrollBuffer.push(line);
|
|
504
|
+
if (scrollBuffer.length > MAX_SCROLLBACK) {
|
|
505
|
+
scrollBuffer.splice(0, scrollBuffer.length - MAX_SCROLLBACK);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
function setBannerLines(lines) {
|
|
509
|
+
bannerLines = lines;
|
|
510
|
+
}
|
|
511
|
+
// ── Scroll-mode rendering ──────────────────────────────────────────────────
|
|
512
|
+
function viewHeight() {
|
|
513
|
+
return Math.max(3, screenRows() - FIXED_BOTTOM - 1);
|
|
514
|
+
}
|
|
515
|
+
function maxScrollOffset() {
|
|
516
|
+
return Math.max(0, scrollBuffer.length - viewHeight());
|
|
517
|
+
}
|
|
518
|
+
function renderScrollView() {
|
|
519
|
+
const bottomRows = buildBottomRows();
|
|
520
|
+
const vh = viewHeight();
|
|
521
|
+
scrollOffset = Math.max(0, Math.min(scrollOffset, maxScrollOffset()));
|
|
522
|
+
const start = Math.max(0, scrollBuffer.length - vh - scrollOffset);
|
|
523
|
+
const visible = scrollBuffer.slice(start, start + vh);
|
|
524
|
+
const width = Math.max(10, cols() - MARGIN);
|
|
525
|
+
const sr = screenRows();
|
|
526
|
+
rawWrite('\x1b[2J\x1b[H');
|
|
527
|
+
// Banner
|
|
528
|
+
const withBanner = bannerLines.length > 0 && sr - bannerLines.length - FIXED_BOTTOM - 1 >= 3;
|
|
529
|
+
if (withBanner) {
|
|
530
|
+
bannerLines.forEach(line => { rawWrite(truncVisible(line, width)); rawWrite('\n'); });
|
|
531
|
+
}
|
|
532
|
+
visible.forEach(line => { rawWrite(truncVisible(line, width)); rawWrite('\n'); });
|
|
533
|
+
for (let i = visible.length; i < vh; i++)
|
|
534
|
+
rawWrite('\n');
|
|
535
|
+
// Bottom block
|
|
536
|
+
const startRow = sr - FIXED_BOTTOM + 1;
|
|
537
|
+
for (let i = 0; i < bottomRows.length; i++) {
|
|
538
|
+
rawWrite(`\x1b[${startRow + i};1H`);
|
|
539
|
+
rawWrite(bottomRows[i]);
|
|
540
|
+
}
|
|
541
|
+
const bottom = start + visible.length;
|
|
542
|
+
const pos = scrollOffset === 0 ? 'BOT' : start === 0 ? 'TOP' : `${Math.round((bottom / Math.max(1, scrollBuffer.length)) * 100)}%`;
|
|
543
|
+
const indicator = ACCENT.bold(' -- SCROLL -- ')
|
|
544
|
+
+ TEXT_DIM(`${start + 1}-${bottom}/${scrollBuffer.length} ${pos} · j/k · ^d/^u · gg/G · i/Enter/Esc insert`);
|
|
545
|
+
rawWrite(`\x1b[${sr};1H`);
|
|
546
|
+
rawWrite(truncVisible(indicator, width));
|
|
547
|
+
}
|
|
548
|
+
function enterScrollMode(initialOffset) {
|
|
549
|
+
if (scrollBuffer.length === 0)
|
|
550
|
+
return;
|
|
551
|
+
scrollMode = true;
|
|
552
|
+
pendingG = false;
|
|
553
|
+
scrollOffset = initialOffset;
|
|
554
|
+
renderScrollView();
|
|
555
|
+
}
|
|
556
|
+
function exitScrollMode() {
|
|
557
|
+
scrollMode = false;
|
|
558
|
+
pendingG = false;
|
|
559
|
+
scrollOffset = 0;
|
|
560
|
+
redrawLiveView();
|
|
561
|
+
}
|
|
562
|
+
function redrawLiveView() {
|
|
563
|
+
const width = Math.max(10, cols() - MARGIN);
|
|
564
|
+
const sr = screenRows();
|
|
565
|
+
rawWrite('\x1b[2J\x1b[H');
|
|
566
|
+
// Banner
|
|
567
|
+
const withBanner = bannerLines.length > 0 && sr - bannerLines.length - FIXED_BOTTOM - 1 >= 3;
|
|
568
|
+
const banner = withBanner ? bannerLines : [];
|
|
569
|
+
banner.forEach(line => { rawWrite(truncVisible(line, width)); rawWrite('\n'); });
|
|
570
|
+
// Output tail
|
|
571
|
+
const tailMax = Math.max(0, sr - banner.length - FIXED_BOTTOM);
|
|
572
|
+
const tail = scrollBuffer.slice(Math.max(0, scrollBuffer.length - tailMax));
|
|
573
|
+
tail.forEach(line => { rawWrite(truncVisible(line, width)); rawWrite('\n'); });
|
|
574
|
+
// Bottom block
|
|
575
|
+
const bottomRows = buildBottomRows();
|
|
576
|
+
const startRow = sr - FIXED_BOTTOM + 1;
|
|
577
|
+
for (let i = 0; i < bottomRows.length; i++) {
|
|
578
|
+
rawWrite(`\x1b[${startRow + i};1H`);
|
|
579
|
+
clearEol();
|
|
580
|
+
rawWrite(bottomRows[i]);
|
|
581
|
+
}
|
|
582
|
+
// Footer
|
|
583
|
+
if (footerActive) {
|
|
584
|
+
rawWrite(`\x1b[${sr};1H`);
|
|
585
|
+
clearEol();
|
|
586
|
+
rawWrite(truncVisible(footerText, Math.max(10, cols() - MARGIN)));
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// ── Confirm / Input ─────────────────────────────────────────────────────────
|
|
590
|
+
let pendingConfirm = null;
|
|
591
|
+
let pendingInput = null;
|
|
592
|
+
let inputAccumulator = [];
|
|
593
|
+
function askConfirm(message) {
|
|
594
|
+
return new Promise(resolve => {
|
|
595
|
+
writeOutput(chalk_1.default.hex('#d4903a')(` ⚠ ${message} [y/N]`));
|
|
596
|
+
pendingConfirm = (answer) => {
|
|
597
|
+
const a = answer.trim().toLowerCase();
|
|
598
|
+
resolve(a === 'y' || a === 'yes');
|
|
599
|
+
};
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
function askInput(prompt) {
|
|
603
|
+
return new Promise(resolve => {
|
|
604
|
+
if (fullscreenPrompt) {
|
|
605
|
+
// In fullscreen mode, write prompt directly and use raw readline-style input
|
|
606
|
+
rawWrite(chalk_1.default.hex('#cc785c')(prompt));
|
|
607
|
+
pendingInput = (text) => {
|
|
608
|
+
rawWrite(text + '\n');
|
|
609
|
+
resolve(text);
|
|
610
|
+
};
|
|
611
|
+
inputAccumulator = [];
|
|
612
|
+
inputBuffer = '';
|
|
613
|
+
cursorPos = 0;
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
writeOutput(chalk_1.default.hex('#cc785c')(prompt));
|
|
617
|
+
pendingInput = (text) => { resolve(text); };
|
|
618
|
+
inputAccumulator = [];
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
// ── Input handling ─────────────────────────────────────────────────────────
|
|
622
|
+
function handleChar(ch) {
|
|
623
|
+
if (ch === '\x03') { // Ctrl-C
|
|
624
|
+
if (overlay !== 'none') {
|
|
625
|
+
closeOverlay();
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (pendingConfirm) {
|
|
629
|
+
const resolve = pendingConfirm;
|
|
630
|
+
pendingConfirm = null;
|
|
631
|
+
inputBuffer = '';
|
|
632
|
+
cursorPos = 0;
|
|
633
|
+
writeOutput(TEXT_DIM('❯ n (cancelled)'));
|
|
634
|
+
resolve('n');
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (pendingInput) {
|
|
638
|
+
const resolve = pendingInput;
|
|
639
|
+
pendingInput = null;
|
|
640
|
+
inputAccumulator = [];
|
|
641
|
+
inputBuffer = '';
|
|
642
|
+
cursorPos = 0;
|
|
643
|
+
writeOutput(TEXT_DIM('❯ (cancelled)'));
|
|
644
|
+
drawPromptBottom();
|
|
645
|
+
resolve('');
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (currentAbort && !currentAbort.signal.aborted) {
|
|
649
|
+
currentAbort.abort();
|
|
650
|
+
writeOutput(chalk_1.default.hex('#d4903a')(' ⏹ Aborting current task...'));
|
|
651
|
+
if (onStop)
|
|
652
|
+
onStop();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
resetScrollRegion();
|
|
656
|
+
unpatchStdout();
|
|
657
|
+
leaveAltScreen();
|
|
658
|
+
showCursor();
|
|
659
|
+
process.exit(0);
|
|
660
|
+
}
|
|
661
|
+
if (ch === '\x04') { // Ctrl-D
|
|
662
|
+
if (pendingInput) {
|
|
663
|
+
const resolve = pendingInput;
|
|
664
|
+
pendingInput = null;
|
|
665
|
+
const finalText = inputAccumulator.join('\n');
|
|
666
|
+
inputAccumulator = [];
|
|
667
|
+
inputBuffer = '';
|
|
668
|
+
cursorPos = 0;
|
|
669
|
+
writeOutput(TEXT_DIM('❯ (end of input)'));
|
|
670
|
+
drawPromptBottom();
|
|
671
|
+
resolve(finalText);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (ch === '\r' || ch === '\n') {
|
|
676
|
+
// Overlay Enter
|
|
677
|
+
if (overlay === 'palette') {
|
|
678
|
+
const commands = (0, command_palette_js_1.filterCommands)(overlayQuery);
|
|
679
|
+
if (commands[overlaySelected]) {
|
|
680
|
+
const cmd = commands[overlaySelected];
|
|
681
|
+
closeOverlay();
|
|
682
|
+
if (onEnter)
|
|
683
|
+
onEnter(cmd.id);
|
|
684
|
+
}
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (overlay === 'session') {
|
|
688
|
+
closeOverlay();
|
|
689
|
+
if (onSessionSwitch)
|
|
690
|
+
onSessionSwitch();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const line = inputBuffer.trim();
|
|
694
|
+
if (pendingConfirm) {
|
|
695
|
+
const resolve = pendingConfirm;
|
|
696
|
+
pendingConfirm = null;
|
|
697
|
+
writeOutput(TEXT_DIM(`❯ ${line || 'n'}`));
|
|
698
|
+
inputBuffer = '';
|
|
699
|
+
cursorPos = 0;
|
|
700
|
+
drawPromptBottom();
|
|
701
|
+
resolve(line);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (pendingInput) {
|
|
705
|
+
if (fullscreenPrompt) {
|
|
706
|
+
// Fullscreen mode: single-line submit on Enter
|
|
707
|
+
const resolve = pendingInput;
|
|
708
|
+
pendingInput = null;
|
|
709
|
+
const line = inputBuffer;
|
|
710
|
+
if (line)
|
|
711
|
+
rawWrite(line + '\n');
|
|
712
|
+
inputBuffer = '';
|
|
713
|
+
cursorPos = 0;
|
|
714
|
+
resolve(line);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
// Normal TUI mode: accumulate lines, Ctrl-D submits
|
|
718
|
+
inputAccumulator.push(inputBuffer);
|
|
719
|
+
inputBuffer = '';
|
|
720
|
+
cursorPos = 0;
|
|
721
|
+
drawPromptBottom();
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (line) {
|
|
725
|
+
echoUserLine(line);
|
|
726
|
+
}
|
|
727
|
+
inputBuffer = '';
|
|
728
|
+
cursorPos = 0;
|
|
729
|
+
drawPromptBottom();
|
|
730
|
+
if (line && onEnter)
|
|
731
|
+
onEnter(line);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (ch === '\x7f' || ch === '\b') {
|
|
735
|
+
if (overlay !== 'none') {
|
|
736
|
+
if (overlayQuery.length > 0) {
|
|
737
|
+
overlayQuery = overlayQuery.slice(0, -1);
|
|
738
|
+
overlaySelected = 0;
|
|
739
|
+
drawPromptBottom();
|
|
740
|
+
}
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (cursorPos > 0) {
|
|
744
|
+
inputBuffer = inputBuffer.slice(0, cursorPos - 1) + inputBuffer.slice(cursorPos);
|
|
745
|
+
cursorPos--;
|
|
746
|
+
if (fullscreenPrompt && pendingInput) {
|
|
747
|
+
rawWrite('\b \b');
|
|
748
|
+
}
|
|
749
|
+
drawPromptBottom();
|
|
750
|
+
}
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (ch === '\x15') { // Ctrl-U
|
|
754
|
+
if (overlay !== 'none') {
|
|
755
|
+
overlayQuery = '';
|
|
756
|
+
overlaySelected = 0;
|
|
757
|
+
drawPromptBottom();
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
inputBuffer = '';
|
|
761
|
+
cursorPos = 0;
|
|
762
|
+
drawPromptBottom();
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (ch === '\t') { // Tab — agent mode switcher
|
|
766
|
+
agentMode = agentMode === 'build' ? 'plan' : 'build';
|
|
767
|
+
if (onModeChange)
|
|
768
|
+
onModeChange(agentMode);
|
|
769
|
+
drawPromptBottom();
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
if (ch >= ' ' && ch <= '~') {
|
|
773
|
+
if (overlay !== 'none') {
|
|
774
|
+
overlayQuery += ch;
|
|
775
|
+
overlaySelected = 0;
|
|
776
|
+
drawPromptBottom();
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
inputBuffer = inputBuffer.slice(0, cursorPos) + ch + inputBuffer.slice(cursorPos);
|
|
780
|
+
cursorPos++;
|
|
781
|
+
if (fullscreenPrompt && pendingInput) {
|
|
782
|
+
// Echo typed char directly in fullscreen mode
|
|
783
|
+
rawWrite(ch);
|
|
784
|
+
}
|
|
785
|
+
drawPromptBottom();
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function handleScrollKey(key) {
|
|
790
|
+
const half = Math.floor(viewHeight() / 2);
|
|
791
|
+
const gWasPending = pendingG;
|
|
792
|
+
pendingG = false;
|
|
793
|
+
switch (key) {
|
|
794
|
+
case 'g':
|
|
795
|
+
if (!gWasPending) {
|
|
796
|
+
pendingG = true;
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
scrollOffset = maxScrollOffset();
|
|
800
|
+
break;
|
|
801
|
+
case 'k':
|
|
802
|
+
case '\x1b[A':
|
|
803
|
+
scrollOffset += 1;
|
|
804
|
+
break;
|
|
805
|
+
case 'j':
|
|
806
|
+
case '\x1b[B':
|
|
807
|
+
scrollOffset -= 1;
|
|
808
|
+
break;
|
|
809
|
+
case '\x15':
|
|
810
|
+
case '\x1b[5~':
|
|
811
|
+
scrollOffset += half;
|
|
812
|
+
break;
|
|
813
|
+
case '\x04':
|
|
814
|
+
case '\x1b[6~':
|
|
815
|
+
scrollOffset -= half;
|
|
816
|
+
break;
|
|
817
|
+
case 'G':
|
|
818
|
+
scrollOffset = 0;
|
|
819
|
+
break;
|
|
820
|
+
case 'i':
|
|
821
|
+
case 'q':
|
|
822
|
+
case '\x1b':
|
|
823
|
+
case '\r':
|
|
824
|
+
case '\n':
|
|
825
|
+
case '\x03':
|
|
826
|
+
exitScrollMode();
|
|
827
|
+
return;
|
|
828
|
+
default: return;
|
|
829
|
+
}
|
|
830
|
+
renderScrollView();
|
|
831
|
+
}
|
|
832
|
+
function handleKey(key) {
|
|
833
|
+
if (scrollMode) {
|
|
834
|
+
handleScrollKey(key);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
// Ctrl+P — command palette (0x10)
|
|
838
|
+
if (key === '\x10') {
|
|
839
|
+
openOverlay('palette');
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
// Ctrl+L — session switcher (0x0c)
|
|
843
|
+
if (key === '\x0c') {
|
|
844
|
+
openOverlay('session');
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (key === '\x1b') {
|
|
848
|
+
if (overlay !== 'none') {
|
|
849
|
+
closeOverlay();
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
enterScrollMode(0);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (key === '\x1b[A' && inputBuffer.length === 0) {
|
|
856
|
+
enterScrollMode(1);
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (key === '\x1b[5~') {
|
|
860
|
+
enterScrollMode(Math.floor(viewHeight() / 2));
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (key === '\x1b[D') {
|
|
864
|
+
if (overlay !== 'none')
|
|
865
|
+
return;
|
|
866
|
+
if (cursorPos > 0) {
|
|
867
|
+
cursorPos--;
|
|
868
|
+
drawPromptBottom();
|
|
869
|
+
}
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
if (key === '\x1b[C') {
|
|
873
|
+
if (overlay !== 'none')
|
|
874
|
+
return;
|
|
875
|
+
if (cursorPos < inputBuffer.length) {
|
|
876
|
+
cursorPos++;
|
|
877
|
+
drawPromptBottom();
|
|
878
|
+
}
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
// Arrow up/down in overlay
|
|
882
|
+
if (overlay !== 'none') {
|
|
883
|
+
if (key === '\x1b[A') {
|
|
884
|
+
overlaySelected = Math.max(0, overlaySelected - 1);
|
|
885
|
+
drawPromptBottom();
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (key === '\x1b[B') {
|
|
889
|
+
const commands = (0, command_palette_js_1.filterCommands)(overlayQuery);
|
|
890
|
+
overlaySelected = Math.min(commands.length - 1, overlaySelected + 1);
|
|
891
|
+
drawPromptBottom();
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (key.length > 1)
|
|
896
|
+
return;
|
|
897
|
+
handleChar(key);
|
|
898
|
+
}
|
|
899
|
+
function rawHandler(data) {
|
|
900
|
+
let i = 0;
|
|
901
|
+
while (i < data.length) {
|
|
902
|
+
if (data[i] === '\x1b' && data[i + 1] === '[') {
|
|
903
|
+
const m = /^\x1b\[[0-9;]*[A-Za-z~]/.exec(data.slice(i));
|
|
904
|
+
if (m) {
|
|
905
|
+
handleKey(m[0]);
|
|
906
|
+
i += m[0].length;
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
handleKey(data[i]);
|
|
911
|
+
i++;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
function startInput() {
|
|
915
|
+
if (inputActive)
|
|
916
|
+
return;
|
|
917
|
+
exports.inputActive = inputActive = true;
|
|
918
|
+
inputBuffer = '';
|
|
919
|
+
cursorPos = 0;
|
|
920
|
+
if (process.stdin.isTTY)
|
|
921
|
+
process.stdin.setRawMode(true);
|
|
922
|
+
process.stdin.resume();
|
|
923
|
+
process.stdin.setEncoding('utf8');
|
|
924
|
+
stdinHandler = rawHandler;
|
|
925
|
+
process.stdin.on('data', stdinHandler);
|
|
926
|
+
}
|
|
927
|
+
function stopInput() {
|
|
928
|
+
if (!inputActive)
|
|
929
|
+
return;
|
|
930
|
+
exports.inputActive = inputActive = false;
|
|
931
|
+
if (stdinHandler) {
|
|
932
|
+
process.stdin.removeListener('data', stdinHandler);
|
|
933
|
+
stdinHandler = null;
|
|
934
|
+
}
|
|
935
|
+
if (process.stdin.isTTY)
|
|
936
|
+
process.stdin.setRawMode(false);
|
|
937
|
+
process.stdin.pause();
|
|
938
|
+
}
|
|
939
|
+
let onEnter = null;
|
|
940
|
+
let onStop = null;
|
|
941
|
+
let currentAbort = null;
|
|
942
|
+
function setCallbacks(opts) {
|
|
943
|
+
onEnter = opts.onEnter;
|
|
944
|
+
onStop = opts.onStop ?? null;
|
|
945
|
+
}
|
|
946
|
+
function setChatId(id) {
|
|
947
|
+
chatId = id;
|
|
948
|
+
}
|
|
949
|
+
function initTui() {
|
|
950
|
+
scrollMode = false;
|
|
951
|
+
scrollOffset = 0;
|
|
952
|
+
pendingG = false;
|
|
953
|
+
scrollBuffer = [];
|
|
954
|
+
streamAccum = '';
|
|
955
|
+
hideCursor();
|
|
956
|
+
patchStdout();
|
|
957
|
+
// Clear screen, draw banner
|
|
958
|
+
rawWrite('\x1b[2J\x1b[H');
|
|
959
|
+
const width = Math.max(10, cols() - MARGIN);
|
|
960
|
+
if (bannerLines.length > 0) {
|
|
961
|
+
bannerLines.forEach(line => { rawWrite(truncVisible(line, width)); rawWrite('\n'); });
|
|
962
|
+
}
|
|
963
|
+
// Set scroll region to exclude bottom FIXED_BOTTOM rows
|
|
964
|
+
setScrollRegion();
|
|
965
|
+
// Draw the bottom block
|
|
966
|
+
drawPromptBottom();
|
|
967
|
+
// Divider in the scroll region
|
|
968
|
+
writeOutput((0, diamond_js_1.gradient)('─'.repeat(Math.min(cols(), 100))));
|
|
969
|
+
}
|
|
970
|
+
function destroyTui() {
|
|
971
|
+
stopToolSpinner();
|
|
972
|
+
scrollMode = false;
|
|
973
|
+
stopInput();
|
|
974
|
+
resetScrollRegion();
|
|
975
|
+
unpatchStdout();
|
|
976
|
+
leaveAltScreen();
|
|
977
|
+
showCursor();
|
|
978
|
+
}
|
|
979
|
+
// ── TUI-aware Display ──────────────────────────────────────────────────────
|
|
980
|
+
function toolIcon(name) {
|
|
981
|
+
const icons = {
|
|
982
|
+
read_file: '📄', list_dir: '📁', edit_file: '✏️',
|
|
983
|
+
write_file: '📝', search_code: '🔍', run_shell: '⚡',
|
|
984
|
+
run_tests: '🧪', git_status: '🌿', git_diff: '📊',
|
|
985
|
+
};
|
|
986
|
+
return icons[name] ?? '🔧';
|
|
987
|
+
}
|
|
988
|
+
function fmtIn(name, input) {
|
|
989
|
+
switch (name) {
|
|
990
|
+
case 'read_file': {
|
|
991
|
+
const r = input.start_line ? ` :${input.start_line}-${input.end_line ?? '?'}` : '';
|
|
992
|
+
return `${input.path}${r}`;
|
|
993
|
+
}
|
|
994
|
+
case 'list_dir': return `${input.path ?? '.'}${input.recursive ? ' (recursive)' : ''}`;
|
|
995
|
+
case 'edit_file': return `${input.path}`;
|
|
996
|
+
case 'write_file': return `${input.path}`;
|
|
997
|
+
case 'search_code': return `"${input.pattern}"${input.path ? ` in ${input.path}` : ''}`;
|
|
998
|
+
case 'run_shell': return String(input.command);
|
|
999
|
+
case 'run_tests': return input.file_or_pattern ? String(input.file_or_pattern) : 'all tests';
|
|
1000
|
+
case 'git_diff': return input.path ? String(input.path) : 'all files';
|
|
1001
|
+
default: return JSON.stringify(input).slice(0, 60);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function sep() {
|
|
1005
|
+
return (0, diamond_js_1.gradient)('─'.repeat(Math.min(cols(), 60)));
|
|
1006
|
+
}
|
|
1007
|
+
function createTuiDisplay() {
|
|
1008
|
+
let inStream = false;
|
|
1009
|
+
let thinkingFrame = 0;
|
|
1010
|
+
let thinkingInterval = null;
|
|
1011
|
+
return {
|
|
1012
|
+
agentThinking() {
|
|
1013
|
+
if (scrollMode)
|
|
1014
|
+
return;
|
|
1015
|
+
if (thinkingInterval)
|
|
1016
|
+
clearInterval(thinkingInterval);
|
|
1017
|
+
const spinners = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
1018
|
+
thinkingInterval = setInterval(() => {
|
|
1019
|
+
if (scrollMode) {
|
|
1020
|
+
if (thinkingInterval) {
|
|
1021
|
+
clearInterval(thinkingInterval);
|
|
1022
|
+
thinkingInterval = null;
|
|
1023
|
+
}
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
const s = spinners[(thinkingFrame++) % spinners.length];
|
|
1027
|
+
cursorCol(1);
|
|
1028
|
+
clearEol();
|
|
1029
|
+
rawWrite(TEXT_DIM(` ${s} thinking`));
|
|
1030
|
+
cursorCol(1);
|
|
1031
|
+
}, 100);
|
|
1032
|
+
},
|
|
1033
|
+
toolStart(name) {
|
|
1034
|
+
if (thinkingInterval) {
|
|
1035
|
+
clearInterval(thinkingInterval);
|
|
1036
|
+
thinkingInterval = null;
|
|
1037
|
+
}
|
|
1038
|
+
stopToolSpinner();
|
|
1039
|
+
startToolSpinner(name);
|
|
1040
|
+
},
|
|
1041
|
+
streamText(text) {
|
|
1042
|
+
if (!inStream) {
|
|
1043
|
+
inStream = true;
|
|
1044
|
+
stopToolSpinner();
|
|
1045
|
+
if (!scrollMode) {
|
|
1046
|
+
cursorCol(1);
|
|
1047
|
+
clearEol();
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
writeStream(TEXT(text));
|
|
1051
|
+
},
|
|
1052
|
+
streamEnd() {
|
|
1053
|
+
if (inStream) {
|
|
1054
|
+
if (streamAccum) {
|
|
1055
|
+
pushScrollback(wrapForTerminal(streamAccum).output.split('\n'));
|
|
1056
|
+
streamAccum = '';
|
|
1057
|
+
}
|
|
1058
|
+
inStream = false;
|
|
1059
|
+
if (scrollMode)
|
|
1060
|
+
return;
|
|
1061
|
+
rawWrite('\n');
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
toolCall(name, input) {
|
|
1065
|
+
stopToolSpinner();
|
|
1066
|
+
const icon = toolIcon(name);
|
|
1067
|
+
const label = chalk_1.default.hex('#cc785c').bold(`${icon} ${name}`);
|
|
1068
|
+
const detail = fmtIn(name, input);
|
|
1069
|
+
writeOutput(` ${label} ${TEXT_DIM(detail)}`);
|
|
1070
|
+
},
|
|
1071
|
+
toolResult(name, result, elapsedMs) {
|
|
1072
|
+
stopToolSpinner();
|
|
1073
|
+
const lines = result.split('\n');
|
|
1074
|
+
const preview = lines.length > 8
|
|
1075
|
+
? lines.slice(0, 8).join('\n') + TEXT_DIM(`\n ... (${lines.length - 8} more lines)`)
|
|
1076
|
+
: result;
|
|
1077
|
+
const elapsed = TEXT_DIM(`${elapsedMs}ms`);
|
|
1078
|
+
const isError = result.startsWith('Error:') || result.startsWith('Tool error');
|
|
1079
|
+
if (isError) {
|
|
1080
|
+
writeOutput(' ' + chalk_1.default.hex('#b15439')('✗ ') + TEXT_DIM(preview.replace(/\n/g, '\n ')));
|
|
1081
|
+
}
|
|
1082
|
+
else {
|
|
1083
|
+
const fl = lines[0] ?? '';
|
|
1084
|
+
if (lines.length <= 3) {
|
|
1085
|
+
writeOutput(' ' + chalk_1.default.hex('#5a9e6e')('✓ ') + TEXT_DIM(result));
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
writeOutput(' ' + chalk_1.default.hex('#5a9e6e')('✓ ') + TEXT_DIM(`${fl}`) + TEXT_DIM(` (+${lines.length - 1} lines) ${elapsed}`));
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
toolBlocked(name, reason) {
|
|
1093
|
+
stopToolSpinner();
|
|
1094
|
+
writeOutput(' ' + chalk_1.default.hex('#d4903a')(`⊘ ${name} blocked: ${reason}`));
|
|
1095
|
+
},
|
|
1096
|
+
warning(msg) {
|
|
1097
|
+
writeOutput('\n' + chalk_1.default.hex('#d4903a')(` ⚠ ${msg}`));
|
|
1098
|
+
},
|
|
1099
|
+
success(msg) {
|
|
1100
|
+
writeOutput('\n' + chalk_1.default.hex('#5a9e6e')(` ✓ ${msg}`));
|
|
1101
|
+
},
|
|
1102
|
+
error(msg) {
|
|
1103
|
+
writeOutput('\n' + chalk_1.default.hex('#b15439')(` ✗ ${msg}`));
|
|
1104
|
+
},
|
|
1105
|
+
header(title, subtitle) {
|
|
1106
|
+
const l = sep();
|
|
1107
|
+
writeOutput('\n' + l);
|
|
1108
|
+
writeOutput(chalk_1.default.hex('#cc785c').bold(` ${title}`));
|
|
1109
|
+
if (subtitle)
|
|
1110
|
+
writeOutput(TEXT_DIM(` ${subtitle}`));
|
|
1111
|
+
writeOutput(l);
|
|
1112
|
+
},
|
|
1113
|
+
summary(text, turns, toolCount) {
|
|
1114
|
+
const l = sep();
|
|
1115
|
+
writeOutput('\n' + l);
|
|
1116
|
+
writeOutput(chalk_1.default.hex('#5a9e6e').bold(' ✓ Done'));
|
|
1117
|
+
writeOutput(TEXT_DIM(` ${turns} turn${turns > 1 ? 's' : ''} · ${toolCount} tool call${toolCount > 1 ? 's' : ''}`));
|
|
1118
|
+
if (text) {
|
|
1119
|
+
const mdLines = (0, markdown_js_1.renderMarkdown)(text);
|
|
1120
|
+
writeOutput('');
|
|
1121
|
+
mdLines.forEach(lx => writeOutput(lx));
|
|
1122
|
+
}
|
|
1123
|
+
writeOutput(l + '\n');
|
|
1124
|
+
},
|
|
1125
|
+
showPlan(plan) {
|
|
1126
|
+
const l = sep();
|
|
1127
|
+
const idxMap = new Map(plan.steps.map((s, i) => [s.id, i + 1]));
|
|
1128
|
+
writeOutput('\n' + l);
|
|
1129
|
+
writeOutput(chalk_1.default.hex('#cc785c').bold(' Execution Plan'));
|
|
1130
|
+
writeOutput(TEXT_DIM(` Goal: ${plan.goal}`));
|
|
1131
|
+
writeOutput(l);
|
|
1132
|
+
plan.steps.forEach((s, i) => {
|
|
1133
|
+
const num = TEXT_DIM(`${i + 1}.`);
|
|
1134
|
+
const spec = chalk_1.default.hex('#cc785c').bold(`[${s.specialist}]`);
|
|
1135
|
+
const task = TEXT(s.task.length > 55 ? s.task.slice(0, 52) + '…' : s.task);
|
|
1136
|
+
const deps = s.dependsOn.length > 0
|
|
1137
|
+
? TEXT_DIM(` ← ${s.dependsOn.map(d => idxMap.get(d) ?? '?').join(', ')}`)
|
|
1138
|
+
: '';
|
|
1139
|
+
writeOutput(` ${num} ${spec} ${task}${deps}`);
|
|
1140
|
+
});
|
|
1141
|
+
writeOutput(l + '\n');
|
|
1142
|
+
},
|
|
1143
|
+
stepStarted(step) {
|
|
1144
|
+
const spec = chalk_1.default.hex('#d4903a').bold(`[${step.specialist}]`);
|
|
1145
|
+
const task = TEXT_DIM(step.task.length > 70 ? step.task.slice(0, 67) + '…' : step.task);
|
|
1146
|
+
writeOutput('\n' + chalk_1.default.hex('#d4903a')(' →') + ` ${spec} ${task}`);
|
|
1147
|
+
},
|
|
1148
|
+
stepCompleted(step, _result) {
|
|
1149
|
+
const spec = chalk_1.default.hex('#5a9e6e').bold(`[${step.specialist}]`);
|
|
1150
|
+
const ms = step.durationMs != null ? `${step.durationMs}ms` : '?ms';
|
|
1151
|
+
writeOutput(chalk_1.default.hex('#5a9e6e')(' ✓') + ` ${spec} ${TEXT_DIM(`done (${ms})`)}`);
|
|
1152
|
+
},
|
|
1153
|
+
retry(info) {
|
|
1154
|
+
const secs = (info.delayMs / 1000).toFixed(1);
|
|
1155
|
+
writeOutput(chalk_1.default.hex('#d4903a')(` ⟳ ${info.provider} retrying in ${secs}s (attempt ${info.attempt}) — ${info.reason}`));
|
|
1156
|
+
},
|
|
1157
|
+
failover(info) {
|
|
1158
|
+
writeOutput(chalk_1.default.hex('#d4903a')(` ⤳ Failing over ${info.from} → ${info.to} (${info.reason})`));
|
|
1159
|
+
},
|
|
1160
|
+
circuit(info) {
|
|
1161
|
+
const colour = info.state === 'open' ? '#b15439' : info.state === 'half-open' ? '#d4903a' : '#5a9e6e';
|
|
1162
|
+
writeOutput(chalk_1.default.hex(colour)(` ◯ Circuit ${info.provider}: ${info.state}`));
|
|
1163
|
+
},
|
|
1164
|
+
contextBar(health) {
|
|
1165
|
+
setFooter((0, context_health_js_1.formatContextBar)(health));
|
|
1166
|
+
},
|
|
1167
|
+
contextDashboard(health) {
|
|
1168
|
+
writeOutput((0, context_health_js_1.formatContextDashboard)(health));
|
|
1169
|
+
},
|
|
1170
|
+
compactionEvent(info) {
|
|
1171
|
+
const saved = ((1 - info.afterTokens / info.beforeTokens) * 100).toFixed(0);
|
|
1172
|
+
writeOutput(chalk_1.default.hex('#d4903a')(` ⚠ Context compacted: ${info.beforeTokens.toLocaleString()} → ${info.afterTokens.toLocaleString()} tokens (-${saved}%) · gen ${info.generation}`));
|
|
1173
|
+
},
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
//# sourceMappingURL=tui.js.map
|