@llmist/cli 15.5.0 → 15.7.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/dist/cli.js +485 -118
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -98,7 +98,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError2 } from "commande
|
|
|
98
98
|
// package.json
|
|
99
99
|
var package_default = {
|
|
100
100
|
name: "@llmist/cli",
|
|
101
|
-
version: "15.
|
|
101
|
+
version: "15.7.0",
|
|
102
102
|
description: "CLI for llmist - run LLM agents from the command line",
|
|
103
103
|
type: "module",
|
|
104
104
|
main: "dist/cli.js",
|
|
@@ -154,7 +154,7 @@ var package_default = {
|
|
|
154
154
|
node: ">=22.0.0"
|
|
155
155
|
},
|
|
156
156
|
dependencies: {
|
|
157
|
-
llmist: "^15.
|
|
157
|
+
llmist: "^15.7.0",
|
|
158
158
|
"@unblessed/node": "^1.0.0-alpha.23",
|
|
159
159
|
chalk: "^5.6.2",
|
|
160
160
|
commander: "^12.1.0",
|
|
@@ -168,7 +168,7 @@ var package_default = {
|
|
|
168
168
|
zod: "^4.1.12"
|
|
169
169
|
},
|
|
170
170
|
devDependencies: {
|
|
171
|
-
"@llmist/testing": "^15.
|
|
171
|
+
"@llmist/testing": "^15.7.0",
|
|
172
172
|
"@types/diff": "^8.0.0",
|
|
173
173
|
"@types/js-yaml": "^4.0.9",
|
|
174
174
|
"@types/marked-terminal": "^6.1.1",
|
|
@@ -4094,6 +4094,7 @@ function configToAgentOptions(config) {
|
|
|
4094
4094
|
if (r["max-timeout"] !== void 0) result.retryMaxTimeout = r["max-timeout"];
|
|
4095
4095
|
if (r.enabled === false) result.noRetry = true;
|
|
4096
4096
|
}
|
|
4097
|
+
if (config["show-hints"] !== void 0) result.showHints = config["show-hints"];
|
|
4097
4098
|
return result;
|
|
4098
4099
|
}
|
|
4099
4100
|
|
|
@@ -4671,6 +4672,10 @@ var BlockRenderer = class _BlockRenderer {
|
|
|
4671
4672
|
currentSessionId = 0;
|
|
4672
4673
|
/** Previous session ID (for deferred cleanup) */
|
|
4673
4674
|
previousSessionId = null;
|
|
4675
|
+
/** Callback for content state changes (empty to non-empty or vice versa) */
|
|
4676
|
+
onHasContentChangeCallback = null;
|
|
4677
|
+
/** Last reported hasContent state (to avoid duplicate callbacks) */
|
|
4678
|
+
lastHasContentState = false;
|
|
4674
4679
|
constructor(container, renderCallback, renderNowCallback) {
|
|
4675
4680
|
this.container = container;
|
|
4676
4681
|
this.renderCallback = renderCallback;
|
|
@@ -4971,6 +4976,16 @@ var BlockRenderer = class _BlockRenderer {
|
|
|
4971
4976
|
child.detach();
|
|
4972
4977
|
}
|
|
4973
4978
|
this.renderCallback();
|
|
4979
|
+
this.notifyHasContentChange();
|
|
4980
|
+
}
|
|
4981
|
+
/**
|
|
4982
|
+
* Set callback for content state changes.
|
|
4983
|
+
* Called when blocks transition from empty to non-empty or vice versa.
|
|
4984
|
+
* Used by HintsBar to know when "^B browse" hint should be shown.
|
|
4985
|
+
*/
|
|
4986
|
+
onHasContentChange(callback) {
|
|
4987
|
+
this.onHasContentChangeCallback = callback;
|
|
4988
|
+
callback(this.blocks.size > 0);
|
|
4974
4989
|
}
|
|
4975
4990
|
/**
|
|
4976
4991
|
* Start a new session. Called at the start of each REPL turn.
|
|
@@ -5010,6 +5025,7 @@ var BlockRenderer = class _BlockRenderer {
|
|
|
5010
5025
|
}
|
|
5011
5026
|
this.previousSessionId = null;
|
|
5012
5027
|
this.renderCallback();
|
|
5028
|
+
this.notifyHasContentChange();
|
|
5013
5029
|
}
|
|
5014
5030
|
/**
|
|
5015
5031
|
* Get the current session ID (for node creation).
|
|
@@ -5098,6 +5114,17 @@ var BlockRenderer = class _BlockRenderer {
|
|
|
5098
5114
|
// ───────────────────────────────────────────────────────────────────────────
|
|
5099
5115
|
// Private - Node & Block Management
|
|
5100
5116
|
// ───────────────────────────────────────────────────────────────────────────
|
|
5117
|
+
/**
|
|
5118
|
+
* Notify callback if content state has changed.
|
|
5119
|
+
* Only fires when transitioning from empty to non-empty or vice versa.
|
|
5120
|
+
*/
|
|
5121
|
+
notifyHasContentChange() {
|
|
5122
|
+
const hasContent = this.blocks.size > 0;
|
|
5123
|
+
if (hasContent !== this.lastHasContentState) {
|
|
5124
|
+
this.lastHasContentState = hasContent;
|
|
5125
|
+
this.onHasContentChangeCallback?.(hasContent);
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5101
5128
|
generateId(prefix) {
|
|
5102
5129
|
return `${prefix}_${++this.nodeIdCounter}`;
|
|
5103
5130
|
}
|
|
@@ -5124,6 +5151,7 @@ var BlockRenderer = class _BlockRenderer {
|
|
|
5124
5151
|
}
|
|
5125
5152
|
this.applyBottomAlignmentAndScroll();
|
|
5126
5153
|
this.renderCallback();
|
|
5154
|
+
this.notifyHasContentChange();
|
|
5127
5155
|
}
|
|
5128
5156
|
/**
|
|
5129
5157
|
* Render a node and its children recursively.
|
|
@@ -5159,16 +5187,16 @@ var BlockRenderer = class _BlockRenderer {
|
|
|
5159
5187
|
}
|
|
5160
5188
|
/**
|
|
5161
5189
|
* Check if a gadget should render as plain text in focused mode.
|
|
5162
|
-
* TellUser and
|
|
5190
|
+
* TellUser, AskUser, and Finish render as text for a chat-like experience.
|
|
5163
5191
|
*/
|
|
5164
5192
|
shouldRenderAsText(node) {
|
|
5165
5193
|
if (this.contentFilterMode !== "focused") return false;
|
|
5166
5194
|
if (node.type !== "gadget") return false;
|
|
5167
5195
|
const name = node.name;
|
|
5168
|
-
return name === "TellUser" || name === "AskUser";
|
|
5196
|
+
return name === "TellUser" || name === "AskUser" || name === "Finish";
|
|
5169
5197
|
}
|
|
5170
5198
|
/**
|
|
5171
|
-
* Create a text-like block for TellUser/AskUser gadgets in focused mode.
|
|
5199
|
+
* Create a text-like block for TellUser/AskUser/Finish gadgets in focused mode.
|
|
5172
5200
|
* Renders just the content without the gadget header.
|
|
5173
5201
|
*/
|
|
5174
5202
|
createTextLikeBlock(node, top) {
|
|
@@ -5185,6 +5213,13 @@ ${renderMarkdown(message)}
|
|
|
5185
5213
|
if (typeof question === "string") {
|
|
5186
5214
|
content = `
|
|
5187
5215
|
? ${question}
|
|
5216
|
+
`;
|
|
5217
|
+
}
|
|
5218
|
+
} else if (node.name === "Finish") {
|
|
5219
|
+
const message = node.parameters?.message;
|
|
5220
|
+
if (typeof message === "string" && message.trim()) {
|
|
5221
|
+
content = `
|
|
5222
|
+
\x1B[32m\u2713\x1B[0m ${renderMarkdown(message)}
|
|
5188
5223
|
`;
|
|
5189
5224
|
}
|
|
5190
5225
|
}
|
|
@@ -5269,8 +5304,8 @@ ${fullContent}
|
|
|
5269
5304
|
case "system_message": {
|
|
5270
5305
|
const icon = this.getSystemMessageIcon(node.category);
|
|
5271
5306
|
const color = this.getSystemMessageColor(node.category);
|
|
5272
|
-
const
|
|
5273
|
-
return `${indent}${color}${icon} ${node.message}${
|
|
5307
|
+
const RESET3 = "\x1B[0m";
|
|
5308
|
+
return `${indent}${color}${icon} ${node.message}${RESET3}`;
|
|
5274
5309
|
}
|
|
5275
5310
|
}
|
|
5276
5311
|
}
|
|
@@ -5297,7 +5332,7 @@ ${fullContent}
|
|
|
5297
5332
|
getSystemMessageColor(category) {
|
|
5298
5333
|
const YELLOW2 = "\x1B[33m";
|
|
5299
5334
|
const BLUE = "\x1B[34m";
|
|
5300
|
-
const
|
|
5335
|
+
const GRAY2 = "\x1B[90m";
|
|
5301
5336
|
const RED2 = "\x1B[31m";
|
|
5302
5337
|
switch (category) {
|
|
5303
5338
|
case "throttle":
|
|
@@ -5305,7 +5340,7 @@ ${fullContent}
|
|
|
5305
5340
|
case "retry":
|
|
5306
5341
|
return BLUE;
|
|
5307
5342
|
case "info":
|
|
5308
|
-
return
|
|
5343
|
+
return GRAY2;
|
|
5309
5344
|
case "warning":
|
|
5310
5345
|
return YELLOW2;
|
|
5311
5346
|
case "error":
|
|
@@ -5579,6 +5614,7 @@ ${indicator}`;
|
|
|
5579
5614
|
}
|
|
5580
5615
|
this.applyBottomAlignmentAndScroll();
|
|
5581
5616
|
this.renderNowCallback();
|
|
5617
|
+
this.notifyHasContentChange();
|
|
5582
5618
|
}
|
|
5583
5619
|
/**
|
|
5584
5620
|
* Get the current content filter mode.
|
|
@@ -5606,7 +5642,7 @@ ${indicator}`;
|
|
|
5606
5642
|
return false;
|
|
5607
5643
|
case "gadget": {
|
|
5608
5644
|
const name = node.name;
|
|
5609
|
-
return name === "TellUser" || name === "AskUser";
|
|
5645
|
+
return name === "TellUser" || name === "AskUser" || name === "Finish";
|
|
5610
5646
|
}
|
|
5611
5647
|
default:
|
|
5612
5648
|
return false;
|
|
@@ -6036,9 +6072,129 @@ var TUIController = class {
|
|
|
6036
6072
|
}
|
|
6037
6073
|
};
|
|
6038
6074
|
|
|
6075
|
+
// src/tui/hints-bar.ts
|
|
6076
|
+
var GRAY = "\x1B[90m";
|
|
6077
|
+
var RESET = "\x1B[0m";
|
|
6078
|
+
var HintsBar = class {
|
|
6079
|
+
hintsBox;
|
|
6080
|
+
renderCallback;
|
|
6081
|
+
focusMode = "browse";
|
|
6082
|
+
contentFilterMode = "full";
|
|
6083
|
+
hasContent = false;
|
|
6084
|
+
constructor(hintsBox, renderCallback) {
|
|
6085
|
+
this.hintsBox = hintsBox;
|
|
6086
|
+
this.renderCallback = renderCallback;
|
|
6087
|
+
this.render();
|
|
6088
|
+
}
|
|
6089
|
+
/**
|
|
6090
|
+
* Update focus mode and re-render hints.
|
|
6091
|
+
*/
|
|
6092
|
+
setFocusMode(mode) {
|
|
6093
|
+
if (this.focusMode !== mode) {
|
|
6094
|
+
this.focusMode = mode;
|
|
6095
|
+
this.render();
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
/**
|
|
6099
|
+
* Update content filter mode and re-render hints.
|
|
6100
|
+
*/
|
|
6101
|
+
setContentFilterMode(mode) {
|
|
6102
|
+
if (this.contentFilterMode !== mode) {
|
|
6103
|
+
this.contentFilterMode = mode;
|
|
6104
|
+
this.render();
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
/**
|
|
6108
|
+
* Update whether there's content to browse.
|
|
6109
|
+
* Affects whether ^B browse hint is shown in input mode.
|
|
6110
|
+
*/
|
|
6111
|
+
setHasContent(has) {
|
|
6112
|
+
if (this.hasContent !== has) {
|
|
6113
|
+
this.hasContent = has;
|
|
6114
|
+
this.render();
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
/**
|
|
6118
|
+
* Get current focus mode.
|
|
6119
|
+
*/
|
|
6120
|
+
getFocusMode() {
|
|
6121
|
+
return this.focusMode;
|
|
6122
|
+
}
|
|
6123
|
+
/**
|
|
6124
|
+
* Get current content filter mode.
|
|
6125
|
+
*/
|
|
6126
|
+
getContentFilterMode() {
|
|
6127
|
+
return this.contentFilterMode;
|
|
6128
|
+
}
|
|
6129
|
+
/**
|
|
6130
|
+
* Render hints based on current state.
|
|
6131
|
+
*/
|
|
6132
|
+
render() {
|
|
6133
|
+
const hints = [];
|
|
6134
|
+
if (this.contentFilterMode === "focused") {
|
|
6135
|
+
hints.push("^K exit focused mode");
|
|
6136
|
+
} else if (this.focusMode === "input") {
|
|
6137
|
+
hints.push("^S multiline");
|
|
6138
|
+
if (this.hasContent) {
|
|
6139
|
+
hints.push("^B browse");
|
|
6140
|
+
}
|
|
6141
|
+
hints.push("^K focused");
|
|
6142
|
+
} else {
|
|
6143
|
+
hints.push("j/k nav");
|
|
6144
|
+
hints.push("Enter expand");
|
|
6145
|
+
hints.push("^B input");
|
|
6146
|
+
hints.push("^K focused");
|
|
6147
|
+
}
|
|
6148
|
+
this.hintsBox.setContent(`${GRAY}${hints.join(" ")}${RESET}`);
|
|
6149
|
+
this.renderCallback();
|
|
6150
|
+
}
|
|
6151
|
+
};
|
|
6152
|
+
|
|
6153
|
+
// src/tui/editor.ts
|
|
6154
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
6155
|
+
import { readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
6156
|
+
import { tmpdir } from "os";
|
|
6157
|
+
import { join as join3 } from "path";
|
|
6158
|
+
function openEditorSync(initialContent = "") {
|
|
6159
|
+
const editor = process.env.VISUAL || process.env.EDITOR || "vi";
|
|
6160
|
+
const tmpFile = join3(tmpdir(), `llmist-input-${Date.now()}.txt`);
|
|
6161
|
+
writeFileSync2(tmpFile, initialContent, "utf-8");
|
|
6162
|
+
try {
|
|
6163
|
+
const parts = editor.split(/\s+/);
|
|
6164
|
+
const cmd = parts[0];
|
|
6165
|
+
const args = [...parts.slice(1), tmpFile];
|
|
6166
|
+
const result = spawnSync2(cmd, args, {
|
|
6167
|
+
stdio: "inherit",
|
|
6168
|
+
// Connect to user's terminal
|
|
6169
|
+
shell: false
|
|
6170
|
+
});
|
|
6171
|
+
if (result.error) {
|
|
6172
|
+
try {
|
|
6173
|
+
unlinkSync(tmpFile);
|
|
6174
|
+
} catch {
|
|
6175
|
+
}
|
|
6176
|
+
return null;
|
|
6177
|
+
}
|
|
6178
|
+
if (result.status === 0) {
|
|
6179
|
+
const content = readFileSync4(tmpFile, "utf-8");
|
|
6180
|
+
unlinkSync(tmpFile);
|
|
6181
|
+
const trimmed = content.trim();
|
|
6182
|
+
return trimmed || null;
|
|
6183
|
+
} else {
|
|
6184
|
+
unlinkSync(tmpFile);
|
|
6185
|
+
return null;
|
|
6186
|
+
}
|
|
6187
|
+
} catch {
|
|
6188
|
+
try {
|
|
6189
|
+
unlinkSync(tmpFile);
|
|
6190
|
+
} catch {
|
|
6191
|
+
}
|
|
6192
|
+
return null;
|
|
6193
|
+
}
|
|
6194
|
+
}
|
|
6195
|
+
|
|
6039
6196
|
// src/tui/input-handler.ts
|
|
6040
|
-
var
|
|
6041
|
-
var ACTIVE_PROMPT = ">>> ";
|
|
6197
|
+
var PROMPT = "> ";
|
|
6042
6198
|
var InputHandler = class {
|
|
6043
6199
|
inputBar;
|
|
6044
6200
|
promptLabel;
|
|
@@ -6050,6 +6206,14 @@ var InputHandler = class {
|
|
|
6050
6206
|
pendingInput = null;
|
|
6051
6207
|
/** Whether we're waiting for REPL prompt (vs AskUser which should auto-focus) */
|
|
6052
6208
|
isPendingREPLPrompt = false;
|
|
6209
|
+
/** Whether input mode is currently active (focused, capturing keystrokes) */
|
|
6210
|
+
isActive = false;
|
|
6211
|
+
/** Whether a bracketed paste is in progress */
|
|
6212
|
+
isPasting = false;
|
|
6213
|
+
/** Buffer for accumulating bracketed paste content */
|
|
6214
|
+
pasteBuffer = "";
|
|
6215
|
+
/** Flag to indicate content came from editor (skip paste detection on submit) */
|
|
6216
|
+
fromEditor = false;
|
|
6053
6217
|
/** Callback when Ctrl+C is pressed */
|
|
6054
6218
|
ctrlCCallback = null;
|
|
6055
6219
|
/** Callback when Ctrl+B is pressed (toggle focus mode) */
|
|
@@ -6066,16 +6230,23 @@ var InputHandler = class {
|
|
|
6066
6230
|
midSessionHandler = null;
|
|
6067
6231
|
/** Callback to check current focus mode (to avoid conflicts with browse mode) */
|
|
6068
6232
|
getFocusModeCallback = null;
|
|
6069
|
-
|
|
6233
|
+
/** Body height when input bar is visible */
|
|
6234
|
+
bodyHeightWithInput;
|
|
6235
|
+
/** Body height when input bar is hidden (browse mode) */
|
|
6236
|
+
bodyHeightWithoutInput;
|
|
6237
|
+
constructor(inputBar, promptLabel, body, screen, renderCallback, renderNowCallback, hasHints = true) {
|
|
6070
6238
|
this.inputBar = inputBar;
|
|
6071
6239
|
this.promptLabel = promptLabel;
|
|
6072
6240
|
this.body = body;
|
|
6073
6241
|
this.screen = screen;
|
|
6074
6242
|
this.renderCallback = renderCallback;
|
|
6075
6243
|
this.renderNowCallback = renderNowCallback ?? renderCallback;
|
|
6244
|
+
this.bodyHeightWithInput = hasHints ? "100%-3" : "100%-2";
|
|
6245
|
+
this.bodyHeightWithoutInput = hasHints ? "100%-2" : "100%-1";
|
|
6076
6246
|
this.inputBar.on("submit", (value) => {
|
|
6077
6247
|
this.handleSubmit(value);
|
|
6078
6248
|
});
|
|
6249
|
+
this.setupBracketedPasteHandler();
|
|
6079
6250
|
this.inputBar.on("cancel", () => {
|
|
6080
6251
|
this.handleCancel();
|
|
6081
6252
|
});
|
|
@@ -6109,6 +6280,10 @@ var InputHandler = class {
|
|
|
6109
6280
|
this.ctrlPCallback();
|
|
6110
6281
|
}
|
|
6111
6282
|
});
|
|
6283
|
+
this.inputBar.key(["C-s"], () => {
|
|
6284
|
+
const currentValue = this.inputBar.getValue();
|
|
6285
|
+
this.openEditorForInput(currentValue);
|
|
6286
|
+
});
|
|
6112
6287
|
this.screen.key(["enter"], () => {
|
|
6113
6288
|
if (this.isPendingREPLPrompt) {
|
|
6114
6289
|
if (this.getFocusModeCallback?.() === "browse") {
|
|
@@ -6246,37 +6421,54 @@ var InputHandler = class {
|
|
|
6246
6421
|
// Focus Mode API (controlled by TUIApp)
|
|
6247
6422
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
6248
6423
|
/**
|
|
6249
|
-
* Activate input mode -
|
|
6424
|
+
* Activate input mode - focus input bar and capture keyboard.
|
|
6250
6425
|
* Called by TUIApp when switching to input mode.
|
|
6251
|
-
*
|
|
6426
|
+
* Shows input bar with active prompt (">>>") and starts capturing keystrokes.
|
|
6252
6427
|
*/
|
|
6253
6428
|
activate() {
|
|
6254
6429
|
this.isPendingREPLPrompt = false;
|
|
6430
|
+
this.isActive = true;
|
|
6255
6431
|
this.promptLabel.show();
|
|
6256
6432
|
this.inputBar.show();
|
|
6433
|
+
this.body.height = this.bodyHeightWithInput;
|
|
6434
|
+
this.setPrompt(PROMPT);
|
|
6257
6435
|
this.renderNowCallback();
|
|
6258
6436
|
this.inputBar.readInput();
|
|
6259
6437
|
}
|
|
6438
|
+
/** Flag to prevent handleCancel from re-entering during deactivation */
|
|
6439
|
+
isDeactivating = false;
|
|
6260
6440
|
/**
|
|
6261
6441
|
* Deactivate input mode - hide input bar completely.
|
|
6262
6442
|
* Called by TUIApp when switching to browse mode.
|
|
6443
|
+
* Input bar is hidden to give more space to content.
|
|
6263
6444
|
*/
|
|
6264
6445
|
deactivate() {
|
|
6446
|
+
this.isPendingREPLPrompt = false;
|
|
6447
|
+
this.isActive = false;
|
|
6448
|
+
this.isDeactivating = true;
|
|
6449
|
+
this.inputBar.cancel();
|
|
6450
|
+
this.isDeactivating = false;
|
|
6265
6451
|
this.promptLabel.hide();
|
|
6266
6452
|
this.inputBar.hide();
|
|
6267
|
-
this.
|
|
6453
|
+
this.body.height = this.bodyHeightWithoutInput;
|
|
6268
6454
|
this.renderNowCallback();
|
|
6269
6455
|
}
|
|
6270
6456
|
/**
|
|
6271
|
-
* Check if input mode is active (
|
|
6457
|
+
* Check if input mode is active (focused, capturing keystrokes).
|
|
6272
6458
|
*/
|
|
6273
6459
|
isInputActive() {
|
|
6274
|
-
return this.
|
|
6460
|
+
return this.isActive;
|
|
6275
6461
|
}
|
|
6276
6462
|
/**
|
|
6277
6463
|
* Handle input submission.
|
|
6278
6464
|
*/
|
|
6279
6465
|
handleSubmit(rawValue) {
|
|
6466
|
+
if (this.isPasting) {
|
|
6467
|
+
return;
|
|
6468
|
+
}
|
|
6469
|
+
if (this.fromEditor) {
|
|
6470
|
+
this.fromEditor = false;
|
|
6471
|
+
}
|
|
6280
6472
|
const value = rawValue.trim();
|
|
6281
6473
|
if (!value) {
|
|
6282
6474
|
this.inputBar.readInput();
|
|
@@ -6298,18 +6490,93 @@ var InputHandler = class {
|
|
|
6298
6490
|
* Handle input cancellation (ESC key).
|
|
6299
6491
|
*/
|
|
6300
6492
|
handleCancel() {
|
|
6493
|
+
if (this.isDeactivating) {
|
|
6494
|
+
return;
|
|
6495
|
+
}
|
|
6301
6496
|
if (this.pendingInput) {
|
|
6302
6497
|
this.inputBar.readInput();
|
|
6303
6498
|
} else {
|
|
6304
6499
|
this.setIdle();
|
|
6305
6500
|
}
|
|
6306
6501
|
}
|
|
6502
|
+
/**
|
|
6503
|
+
* Set up bracketed paste mode detection.
|
|
6504
|
+
*
|
|
6505
|
+
* Terminal emulators that support bracketed paste send:
|
|
6506
|
+
* - \x1b[200~ before pasted content
|
|
6507
|
+
* - \x1b[201~ after pasted content
|
|
6508
|
+
*
|
|
6509
|
+
* This allows reliable detection of paste vs typed input.
|
|
6510
|
+
*/
|
|
6511
|
+
setupBracketedPasteHandler() {
|
|
6512
|
+
const PASTE_START = "\x1B[200~";
|
|
6513
|
+
const PASTE_END = "\x1B[201~";
|
|
6514
|
+
this.screen.program.input.on("data", (data) => {
|
|
6515
|
+
const str = data.toString();
|
|
6516
|
+
if (str.includes(PASTE_START)) {
|
|
6517
|
+
this.isPasting = true;
|
|
6518
|
+
this.pasteBuffer = "";
|
|
6519
|
+
const startIdx = str.indexOf(PASTE_START) + PASTE_START.length;
|
|
6520
|
+
const afterStart = str.slice(startIdx);
|
|
6521
|
+
if (afterStart.includes(PASTE_END)) {
|
|
6522
|
+
const endIdx = afterStart.indexOf(PASTE_END);
|
|
6523
|
+
this.pasteBuffer = afterStart.slice(0, endIdx);
|
|
6524
|
+
this.isPasting = false;
|
|
6525
|
+
this.handlePaste(this.pasteBuffer);
|
|
6526
|
+
this.pasteBuffer = "";
|
|
6527
|
+
} else {
|
|
6528
|
+
this.pasteBuffer = afterStart;
|
|
6529
|
+
}
|
|
6530
|
+
return;
|
|
6531
|
+
}
|
|
6532
|
+
if (this.isPasting && str.includes(PASTE_END)) {
|
|
6533
|
+
const endIdx = str.indexOf(PASTE_END);
|
|
6534
|
+
this.pasteBuffer += str.slice(0, endIdx);
|
|
6535
|
+
this.isPasting = false;
|
|
6536
|
+
this.handlePaste(this.pasteBuffer);
|
|
6537
|
+
this.pasteBuffer = "";
|
|
6538
|
+
return;
|
|
6539
|
+
}
|
|
6540
|
+
if (this.isPasting) {
|
|
6541
|
+
this.pasteBuffer += str;
|
|
6542
|
+
}
|
|
6543
|
+
});
|
|
6544
|
+
}
|
|
6545
|
+
/**
|
|
6546
|
+
* Handle completed paste content.
|
|
6547
|
+
*
|
|
6548
|
+
* If content contains newlines, opens $EDITOR for multiline editing.
|
|
6549
|
+
* Otherwise, inserts directly into the input bar.
|
|
6550
|
+
*/
|
|
6551
|
+
handlePaste(content) {
|
|
6552
|
+
if (!content) return;
|
|
6553
|
+
if (content.includes("\n")) {
|
|
6554
|
+
const currentValue = this.inputBar.getValue();
|
|
6555
|
+
this.openEditorForInput(currentValue + content);
|
|
6556
|
+
} else {
|
|
6557
|
+
const currentValue = this.inputBar.getValue();
|
|
6558
|
+
this.inputBar.setValue(currentValue + content);
|
|
6559
|
+
this.inputBar.readInput();
|
|
6560
|
+
}
|
|
6561
|
+
}
|
|
6562
|
+
/**
|
|
6563
|
+
* Set prompt text and dynamically adjust layout.
|
|
6564
|
+
* Idle prompt "> " uses 2 chars, active prompt ">>> " uses 4 chars.
|
|
6565
|
+
*/
|
|
6566
|
+
setPrompt(prompt) {
|
|
6567
|
+
const width = prompt.length;
|
|
6568
|
+
this.promptLabel.width = width;
|
|
6569
|
+
this.promptLabel.setContent(prompt);
|
|
6570
|
+
this.inputBar.left = width;
|
|
6571
|
+
this.inputBar.width = `100%-${width}`;
|
|
6572
|
+
}
|
|
6307
6573
|
/**
|
|
6308
6574
|
* Set input to idle state.
|
|
6309
6575
|
*/
|
|
6310
6576
|
setIdle() {
|
|
6311
6577
|
this.isPendingREPLPrompt = false;
|
|
6312
|
-
this.
|
|
6578
|
+
this.isActive = false;
|
|
6579
|
+
this.setPrompt(PROMPT);
|
|
6313
6580
|
this.inputBar.setValue("");
|
|
6314
6581
|
this.renderCallback();
|
|
6315
6582
|
}
|
|
@@ -6327,7 +6594,7 @@ var InputHandler = class {
|
|
|
6327
6594
|
*/
|
|
6328
6595
|
setPendingPrompt() {
|
|
6329
6596
|
this.isPendingREPLPrompt = true;
|
|
6330
|
-
this.
|
|
6597
|
+
this.setPrompt(PROMPT);
|
|
6331
6598
|
this.inputBar.setValue("");
|
|
6332
6599
|
this.renderCallback();
|
|
6333
6600
|
}
|
|
@@ -6336,11 +6603,42 @@ var InputHandler = class {
|
|
|
6336
6603
|
*/
|
|
6337
6604
|
setActive() {
|
|
6338
6605
|
this.isPendingREPLPrompt = false;
|
|
6339
|
-
this.
|
|
6606
|
+
this.isActive = true;
|
|
6607
|
+
this.promptLabel.show();
|
|
6608
|
+
this.inputBar.show();
|
|
6609
|
+
this.body.height = this.bodyHeightWithInput;
|
|
6610
|
+
this.setPrompt(PROMPT);
|
|
6340
6611
|
this.inputBar.setValue("");
|
|
6341
6612
|
this.renderNowCallback();
|
|
6342
6613
|
this.inputBar.readInput();
|
|
6343
6614
|
}
|
|
6615
|
+
/**
|
|
6616
|
+
* Open $EDITOR for multiline input.
|
|
6617
|
+
* Called when user presses Ctrl+S or pastes multiline content.
|
|
6618
|
+
*/
|
|
6619
|
+
openEditorForInput(initialContent) {
|
|
6620
|
+
this.screen.program.clear();
|
|
6621
|
+
this.screen.program.disableMouse();
|
|
6622
|
+
this.screen.program.showCursor();
|
|
6623
|
+
this.screen.program.normalBuffer();
|
|
6624
|
+
const result = openEditorSync(initialContent);
|
|
6625
|
+
this.screen.program.alternateBuffer();
|
|
6626
|
+
this.screen.program.hideCursor();
|
|
6627
|
+
this.screen.program.enableMouse();
|
|
6628
|
+
this.screen.alloc();
|
|
6629
|
+
this.screen.render();
|
|
6630
|
+
this.isPasting = false;
|
|
6631
|
+
this.pasteBuffer = "";
|
|
6632
|
+
setImmediate(() => {
|
|
6633
|
+
if (result) {
|
|
6634
|
+
this.fromEditor = true;
|
|
6635
|
+
this.handleSubmit(result);
|
|
6636
|
+
} else {
|
|
6637
|
+
this.inputBar.setValue(initialContent);
|
|
6638
|
+
this.inputBar.readInput();
|
|
6639
|
+
}
|
|
6640
|
+
});
|
|
6641
|
+
}
|
|
6344
6642
|
};
|
|
6345
6643
|
|
|
6346
6644
|
// src/tui/keymap.ts
|
|
@@ -6383,10 +6681,18 @@ var KeyboardManager = class {
|
|
|
6383
6681
|
onAction({ type: "scroll_page", direction: 1 });
|
|
6384
6682
|
});
|
|
6385
6683
|
screen.key(["up", "k"], () => {
|
|
6684
|
+
if (this.config.getContentFilterMode() === "focused") {
|
|
6685
|
+
onAction({ type: "scroll_line", direction: -1 });
|
|
6686
|
+
return;
|
|
6687
|
+
}
|
|
6386
6688
|
if (this.config.getFocusMode() !== "browse") return;
|
|
6387
6689
|
onAction({ type: "navigation", action: "select_previous" });
|
|
6388
6690
|
});
|
|
6389
6691
|
screen.key(["down", "j"], () => {
|
|
6692
|
+
if (this.config.getContentFilterMode() === "focused") {
|
|
6693
|
+
onAction({ type: "scroll_line", direction: 1 });
|
|
6694
|
+
return;
|
|
6695
|
+
}
|
|
6390
6696
|
if (this.config.getFocusMode() !== "browse") return;
|
|
6391
6697
|
onAction({ type: "navigation", action: "select_next" });
|
|
6392
6698
|
});
|
|
@@ -6446,13 +6752,16 @@ var KeyboardManager = class {
|
|
|
6446
6752
|
|
|
6447
6753
|
// src/tui/layout.ts
|
|
6448
6754
|
import { Box as Box2, ScrollableBox, Text, Textbox } from "@unblessed/node";
|
|
6449
|
-
function createBlockLayout(screen) {
|
|
6755
|
+
function createBlockLayout(screen, showHints = true) {
|
|
6756
|
+
const inputBottom = showHints ? 2 : 1;
|
|
6757
|
+
const statusBottom = showHints ? 1 : 0;
|
|
6758
|
+
const bodyHeight = showHints ? "100%-3" : "100%-2";
|
|
6450
6759
|
const body = new ScrollableBox({
|
|
6451
6760
|
parent: screen,
|
|
6452
6761
|
top: 0,
|
|
6453
6762
|
left: 0,
|
|
6454
6763
|
width: "100%",
|
|
6455
|
-
height:
|
|
6764
|
+
height: bodyHeight,
|
|
6456
6765
|
// Scrolling configuration
|
|
6457
6766
|
scrollable: true,
|
|
6458
6767
|
alwaysScroll: true,
|
|
@@ -6475,10 +6784,10 @@ function createBlockLayout(screen) {
|
|
|
6475
6784
|
});
|
|
6476
6785
|
const promptLabel = new Text({
|
|
6477
6786
|
parent: screen,
|
|
6478
|
-
bottom:
|
|
6787
|
+
bottom: inputBottom,
|
|
6479
6788
|
left: 0,
|
|
6480
|
-
width:
|
|
6481
|
-
// "
|
|
6789
|
+
width: 2,
|
|
6790
|
+
// "> " = 2 chars
|
|
6482
6791
|
height: 1,
|
|
6483
6792
|
content: "> ",
|
|
6484
6793
|
style: {
|
|
@@ -6488,10 +6797,10 @@ function createBlockLayout(screen) {
|
|
|
6488
6797
|
});
|
|
6489
6798
|
const inputBar = new Textbox({
|
|
6490
6799
|
parent: screen,
|
|
6491
|
-
bottom:
|
|
6492
|
-
left:
|
|
6493
|
-
// Position after prompt label
|
|
6494
|
-
width: "100%-
|
|
6800
|
+
bottom: inputBottom,
|
|
6801
|
+
left: 2,
|
|
6802
|
+
// Position after prompt label ("> " = 2 chars)
|
|
6803
|
+
width: "100%-2",
|
|
6495
6804
|
height: 1,
|
|
6496
6805
|
keys: true,
|
|
6497
6806
|
mouse: true,
|
|
@@ -6502,7 +6811,7 @@ function createBlockLayout(screen) {
|
|
|
6502
6811
|
});
|
|
6503
6812
|
const statusBar = new Box2({
|
|
6504
6813
|
parent: screen,
|
|
6505
|
-
bottom:
|
|
6814
|
+
bottom: statusBottom,
|
|
6506
6815
|
left: 0,
|
|
6507
6816
|
width: "100%",
|
|
6508
6817
|
height: 1,
|
|
@@ -6512,7 +6821,19 @@ function createBlockLayout(screen) {
|
|
|
6512
6821
|
bg: "black"
|
|
6513
6822
|
}
|
|
6514
6823
|
});
|
|
6515
|
-
|
|
6824
|
+
const hintsBar = showHints ? new Box2({
|
|
6825
|
+
parent: screen,
|
|
6826
|
+
bottom: 0,
|
|
6827
|
+
left: 0,
|
|
6828
|
+
width: "100%",
|
|
6829
|
+
height: 1,
|
|
6830
|
+
tags: false,
|
|
6831
|
+
style: {
|
|
6832
|
+
fg: "white",
|
|
6833
|
+
bg: "black"
|
|
6834
|
+
}
|
|
6835
|
+
}) : null;
|
|
6836
|
+
return { body, promptLabel, inputBar, statusBar, hintsBar };
|
|
6516
6837
|
}
|
|
6517
6838
|
|
|
6518
6839
|
// src/tui/approval-dialog.ts
|
|
@@ -6633,7 +6954,7 @@ function escapeContent(str) {
|
|
|
6633
6954
|
|
|
6634
6955
|
// src/tui/raw-viewer.ts
|
|
6635
6956
|
import { Box as Box4 } from "@unblessed/node";
|
|
6636
|
-
var
|
|
6957
|
+
var RESET2 = "\x1B[0m";
|
|
6637
6958
|
var BOLD = "\x1B[1m";
|
|
6638
6959
|
var DIM = "\x1B[2m";
|
|
6639
6960
|
var RED = "\x1B[31m";
|
|
@@ -6665,17 +6986,17 @@ function showRawViewer(options) {
|
|
|
6665
6986
|
if (mode === "request") {
|
|
6666
6987
|
title = ` Raw Parameters - ${gadgetName} `;
|
|
6667
6988
|
if (!parameters || Object.keys(parameters).length === 0) {
|
|
6668
|
-
content = `${DIM}No parameters${
|
|
6989
|
+
content = `${DIM}No parameters${RESET2}`;
|
|
6669
6990
|
} else {
|
|
6670
6991
|
content = formatGadgetParameters(parameters);
|
|
6671
6992
|
}
|
|
6672
6993
|
} else {
|
|
6673
6994
|
title = ` Raw Result - ${gadgetName} `;
|
|
6674
6995
|
if (error) {
|
|
6675
|
-
content = `${RED}${BOLD}Error:${
|
|
6996
|
+
content = `${RED}${BOLD}Error:${RESET2}
|
|
6676
6997
|
${error}`;
|
|
6677
6998
|
} else if (!result) {
|
|
6678
|
-
content = `${DIM}No result data available${
|
|
6999
|
+
content = `${DIM}No result data available${RESET2}`;
|
|
6679
7000
|
} else {
|
|
6680
7001
|
content = formatGadgetResult(result);
|
|
6681
7002
|
}
|
|
@@ -6684,14 +7005,14 @@ ${error}`;
|
|
|
6684
7005
|
if (mode === "request") {
|
|
6685
7006
|
title = ` Raw Request - #${iteration} ${model} `;
|
|
6686
7007
|
if (!request || request.length === 0) {
|
|
6687
|
-
content = `${DIM}No request data available${
|
|
7008
|
+
content = `${DIM}No request data available${RESET2}`;
|
|
6688
7009
|
} else {
|
|
6689
7010
|
content = formatMessages(request);
|
|
6690
7011
|
}
|
|
6691
7012
|
} else {
|
|
6692
7013
|
title = ` Raw Response - #${iteration} ${model} `;
|
|
6693
7014
|
if (!response) {
|
|
6694
|
-
content = `${DIM}No response data available${
|
|
7015
|
+
content = `${DIM}No response data available${RESET2}`;
|
|
6695
7016
|
} else {
|
|
6696
7017
|
content = response;
|
|
6697
7018
|
}
|
|
@@ -6732,7 +7053,7 @@ ${error}`;
|
|
|
6732
7053
|
left: 0,
|
|
6733
7054
|
width: "100%",
|
|
6734
7055
|
height: 1,
|
|
6735
|
-
content: `${DIM} [${WHITE}\u2191/\u2193/PgUp/PgDn${DIM}] Scroll [${WHITE}Home/End${DIM}] Jump [${WHITE}Escape/q${DIM}] Close${
|
|
7056
|
+
content: `${DIM} [${WHITE}\u2191/\u2193/PgUp/PgDn${DIM}] Scroll [${WHITE}Home/End${DIM}] Jump [${WHITE}Escape/q${DIM}] Close${RESET2}`,
|
|
6736
7057
|
tags: false,
|
|
6737
7058
|
style: { fg: "white", bg: "black" }
|
|
6738
7059
|
});
|
|
@@ -6764,11 +7085,11 @@ function formatMessages(messages) {
|
|
|
6764
7085
|
const msg = messages[i];
|
|
6765
7086
|
const roleColor = getRoleColor(msg.role);
|
|
6766
7087
|
const roleName = msg.role.toUpperCase();
|
|
6767
|
-
lines.push(`${DIM}${separator}${
|
|
7088
|
+
lines.push(`${DIM}${separator}${RESET2}`);
|
|
6768
7089
|
lines.push(
|
|
6769
|
-
`${roleColor}${BOLD}[${roleName}]${
|
|
7090
|
+
`${roleColor}${BOLD}[${roleName}]${RESET2} ${DIM}Message ${i + 1} of ${messages.length}${RESET2}`
|
|
6770
7091
|
);
|
|
6771
|
-
lines.push(`${DIM}${separator}${
|
|
7092
|
+
lines.push(`${DIM}${separator}${RESET2}`);
|
|
6772
7093
|
lines.push("");
|
|
6773
7094
|
const contentLines = formatMessageContent(msg.content);
|
|
6774
7095
|
lines.push(...contentLines);
|
|
@@ -6791,19 +7112,19 @@ function formatMessageContent(content) {
|
|
|
6791
7112
|
lines.push(...part.text.split("\n"));
|
|
6792
7113
|
} else if (isImagePart(part)) {
|
|
6793
7114
|
const mediaType = part.source?.media_type || "unknown";
|
|
6794
|
-
lines.push(`${DIM}[Image: ${mediaType}]${
|
|
7115
|
+
lines.push(`${DIM}[Image: ${mediaType}]${RESET2}`);
|
|
6795
7116
|
} else if (isAudioPart(part)) {
|
|
6796
7117
|
const mediaType = part.source?.media_type || "unknown";
|
|
6797
|
-
lines.push(`${DIM}[Audio: ${mediaType}]${
|
|
7118
|
+
lines.push(`${DIM}[Audio: ${mediaType}]${RESET2}`);
|
|
6798
7119
|
} else if (isToolUsePart(part)) {
|
|
6799
|
-
lines.push(`${YELLOW}${BOLD}[Tool Use: ${part.name}]${
|
|
6800
|
-
lines.push(`${DIM}ID: ${part.id}${
|
|
6801
|
-
lines.push(`${DIM}Input:${
|
|
7120
|
+
lines.push(`${YELLOW}${BOLD}[Tool Use: ${part.name}]${RESET2}`);
|
|
7121
|
+
lines.push(`${DIM}ID: ${part.id}${RESET2}`);
|
|
7122
|
+
lines.push(`${DIM}Input:${RESET2}`);
|
|
6802
7123
|
const inputStr = JSON.stringify(part.input, null, 2);
|
|
6803
7124
|
lines.push(...inputStr.split("\n").map((l) => ` ${l}`));
|
|
6804
7125
|
} else if (isToolResultPart(part)) {
|
|
6805
|
-
lines.push(`${CYAN}${BOLD}[Tool Result]${
|
|
6806
|
-
lines.push(`${DIM}Tool Use ID: ${part.tool_use_id}${
|
|
7126
|
+
lines.push(`${CYAN}${BOLD}[Tool Result]${RESET2}`);
|
|
7127
|
+
lines.push(`${DIM}Tool Use ID: ${part.tool_use_id}${RESET2}`);
|
|
6807
7128
|
if (typeof part.content === "string") {
|
|
6808
7129
|
lines.push(...part.content.split("\n"));
|
|
6809
7130
|
} else {
|
|
@@ -6811,7 +7132,7 @@ function formatMessageContent(content) {
|
|
|
6811
7132
|
}
|
|
6812
7133
|
} else {
|
|
6813
7134
|
const partType = part.type || "unknown";
|
|
6814
|
-
lines.push(`${DIM}[${partType}]${
|
|
7135
|
+
lines.push(`${DIM}[${partType}]${RESET2}`);
|
|
6815
7136
|
lines.push(JSON.stringify(part, null, 2));
|
|
6816
7137
|
}
|
|
6817
7138
|
}
|
|
@@ -6847,9 +7168,9 @@ function isToolResultPart(part) {
|
|
|
6847
7168
|
function formatGadgetParameters(params) {
|
|
6848
7169
|
const lines = [];
|
|
6849
7170
|
const separator = "\u2500".repeat(78);
|
|
6850
|
-
lines.push(`${DIM}${separator}${
|
|
6851
|
-
lines.push(`${CYAN}${BOLD}Parameters${
|
|
6852
|
-
lines.push(`${DIM}${separator}${
|
|
7171
|
+
lines.push(`${DIM}${separator}${RESET2}`);
|
|
7172
|
+
lines.push(`${CYAN}${BOLD}Parameters${RESET2}`);
|
|
7173
|
+
lines.push(`${DIM}${separator}${RESET2}`);
|
|
6853
7174
|
lines.push("");
|
|
6854
7175
|
const json = JSON.stringify(params, null, 2);
|
|
6855
7176
|
const highlighted = highlightJson(json);
|
|
@@ -6859,9 +7180,9 @@ function formatGadgetParameters(params) {
|
|
|
6859
7180
|
function formatGadgetResult(result) {
|
|
6860
7181
|
const lines = [];
|
|
6861
7182
|
const separator = "\u2500".repeat(78);
|
|
6862
|
-
lines.push(`${DIM}${separator}${
|
|
6863
|
-
lines.push(`${GREEN}${BOLD}Result${
|
|
6864
|
-
lines.push(`${DIM}${separator}${
|
|
7183
|
+
lines.push(`${DIM}${separator}${RESET2}`);
|
|
7184
|
+
lines.push(`${GREEN}${BOLD}Result${RESET2}`);
|
|
7185
|
+
lines.push(`${DIM}${separator}${RESET2}`);
|
|
6865
7186
|
lines.push("");
|
|
6866
7187
|
const trimmed = result.trim();
|
|
6867
7188
|
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
@@ -6877,10 +7198,10 @@ function formatGadgetResult(result) {
|
|
|
6877
7198
|
return lines.join("\n");
|
|
6878
7199
|
}
|
|
6879
7200
|
function highlightJson(json) {
|
|
6880
|
-
let result = json.replace(/"([^"]+)":/g, `${CYAN}"$1"${
|
|
6881
|
-
result = result.replace(/: "([^"]*)"/g, `: ${GREEN}"$1"${
|
|
6882
|
-
result = result.replace(/: (-?\d+\.?\d*)/g, `: ${YELLOW}$1${
|
|
6883
|
-
result = result.replace(/: (true|false|null)/g, `: ${MAGENTA}$1${
|
|
7201
|
+
let result = json.replace(/"([^"]+)":/g, `${CYAN}"$1"${RESET2}:`);
|
|
7202
|
+
result = result.replace(/: "([^"]*)"/g, `: ${GREEN}"$1"${RESET2}`);
|
|
7203
|
+
result = result.replace(/: (-?\d+\.?\d*)/g, `: ${YELLOW}$1${RESET2}`);
|
|
7204
|
+
result = result.replace(/: (true|false|null)/g, `: ${MAGENTA}$1${RESET2}`);
|
|
6884
7205
|
return result;
|
|
6885
7206
|
}
|
|
6886
7207
|
|
|
@@ -6959,6 +7280,7 @@ function createScreen(options) {
|
|
|
6959
7280
|
// Use alternate screen buffer (restores on exit)
|
|
6960
7281
|
useBCE: true
|
|
6961
7282
|
});
|
|
7283
|
+
screen.program.write("\x1B[?2004h");
|
|
6962
7284
|
let isDestroyed = false;
|
|
6963
7285
|
let renderPending = false;
|
|
6964
7286
|
let renderTimeout = null;
|
|
@@ -6989,6 +7311,7 @@ function createScreen(options) {
|
|
|
6989
7311
|
clearTimeout(renderTimeout);
|
|
6990
7312
|
renderTimeout = null;
|
|
6991
7313
|
}
|
|
7314
|
+
screen.program.write("\x1B[?2004l");
|
|
6992
7315
|
screen.destroy();
|
|
6993
7316
|
process.stdout.write("\x1B[?25h");
|
|
6994
7317
|
};
|
|
@@ -7030,7 +7353,7 @@ var StatusBar = class {
|
|
|
7030
7353
|
streamingOutputTokens = 0;
|
|
7031
7354
|
/** Whether we're currently streaming */
|
|
7032
7355
|
isStreaming = false;
|
|
7033
|
-
/** Active LLM calls: Map from label ("#1") to model name */
|
|
7356
|
+
/** Active LLM calls: Map from label ("#1") to model name and start time */
|
|
7034
7357
|
activeLLMCalls = /* @__PURE__ */ new Map();
|
|
7035
7358
|
/** Active gadgets (by name) */
|
|
7036
7359
|
activeGadgets = /* @__PURE__ */ new Set();
|
|
@@ -7127,7 +7450,7 @@ var StatusBar = class {
|
|
|
7127
7450
|
* @param model - Full model name like "gemini:gemini-2.5-flash"
|
|
7128
7451
|
*/
|
|
7129
7452
|
startLLMCall(label, model) {
|
|
7130
|
-
this.activeLLMCalls.set(label, model);
|
|
7453
|
+
this.activeLLMCalls.set(label, { model, startTime: Date.now() });
|
|
7131
7454
|
this.startSpinner();
|
|
7132
7455
|
this.render();
|
|
7133
7456
|
}
|
|
@@ -7140,6 +7463,19 @@ var StatusBar = class {
|
|
|
7140
7463
|
this.maybeStopSpinner();
|
|
7141
7464
|
this.render();
|
|
7142
7465
|
}
|
|
7466
|
+
/**
|
|
7467
|
+
* Get the start time of the earliest running LLM call.
|
|
7468
|
+
* Used to show elapsed time since the first call started (for concurrent subagents).
|
|
7469
|
+
* @returns Start time in ms, or null if no LLM calls are active
|
|
7470
|
+
*/
|
|
7471
|
+
getEarliestLLMCallStartTime() {
|
|
7472
|
+
if (this.activeLLMCalls.size === 0) return null;
|
|
7473
|
+
let earliest = Infinity;
|
|
7474
|
+
for (const { startTime } of this.activeLLMCalls.values()) {
|
|
7475
|
+
if (startTime < earliest) earliest = startTime;
|
|
7476
|
+
}
|
|
7477
|
+
return earliest;
|
|
7478
|
+
}
|
|
7143
7479
|
/**
|
|
7144
7480
|
* Track a gadget as active.
|
|
7145
7481
|
* @param name - Gadget name like "ReadFile" or "BrowseWeb"
|
|
@@ -7271,6 +7607,15 @@ var StatusBar = class {
|
|
|
7271
7607
|
this.startLLMCall(label, event.model);
|
|
7272
7608
|
break;
|
|
7273
7609
|
}
|
|
7610
|
+
case "llm_response_end": {
|
|
7611
|
+
const label = this.nodeIdToLabel.get(event.nodeId);
|
|
7612
|
+
if (label) {
|
|
7613
|
+
this.activeLLMCalls.delete(label);
|
|
7614
|
+
this.maybeStopSpinner();
|
|
7615
|
+
this.render();
|
|
7616
|
+
}
|
|
7617
|
+
break;
|
|
7618
|
+
}
|
|
7274
7619
|
case "llm_call_complete": {
|
|
7275
7620
|
const label = this.nodeIdToLabel.get(event.nodeId);
|
|
7276
7621
|
if (label) {
|
|
@@ -7414,68 +7759,72 @@ var StatusBar = class {
|
|
|
7414
7759
|
* @param immediate - If true, render immediately without debouncing
|
|
7415
7760
|
*/
|
|
7416
7761
|
render(immediate = false) {
|
|
7417
|
-
const elapsed = this.getElapsedSeconds().toFixed(1);
|
|
7418
7762
|
const YELLOW2 = "\x1B[33m";
|
|
7419
7763
|
const GREEN2 = "\x1B[32m";
|
|
7420
7764
|
const BLUE = "\x1B[34m";
|
|
7421
7765
|
const CYAN2 = "\x1B[36m";
|
|
7422
7766
|
const MAGENTA2 = "\x1B[35m";
|
|
7423
|
-
const
|
|
7424
|
-
const
|
|
7767
|
+
const GRAY2 = "\x1B[90m";
|
|
7768
|
+
const RESET3 = "\x1B[0m";
|
|
7425
7769
|
const BG_BLUE = "\x1B[44m";
|
|
7426
|
-
const BG_GREEN = "\x1B[42m";
|
|
7427
7770
|
const WHITE2 = "\x1B[37m";
|
|
7428
|
-
const BLACK = "\x1B[30m";
|
|
7429
7771
|
const displayInputTokens = this.metrics.inputTokens + this.streamingInputTokens;
|
|
7430
7772
|
const displayOutputTokens = this.metrics.outputTokens + this.streamingOutputTokens;
|
|
7431
7773
|
const parts = [];
|
|
7432
|
-
if (this.
|
|
7433
|
-
parts.push(`${BG_BLUE}${WHITE2}
|
|
7434
|
-
} else if (this.focusMode === "browse") {
|
|
7435
|
-
parts.push(`${BG_BLUE}${WHITE2} BROWSE ${RESET2}`);
|
|
7436
|
-
} else {
|
|
7437
|
-
parts.push(`${BG_GREEN}${BLACK} INPUT ${RESET2}`);
|
|
7774
|
+
if (this.focusMode === "browse" && this.contentFilterMode !== "focused") {
|
|
7775
|
+
parts.push(`${BG_BLUE}${WHITE2} BROWSE ${RESET3}`);
|
|
7438
7776
|
}
|
|
7439
7777
|
if (this.profiles.length > 0) {
|
|
7440
7778
|
const profile = this.profiles[this.currentProfileIndex];
|
|
7441
7779
|
const display = profile.length > 12 ? `${profile.slice(0, 11)}\u2026` : profile;
|
|
7442
|
-
parts.push(`${YELLOW2}${display}${
|
|
7780
|
+
parts.push(`${YELLOW2}${display}${RESET3}`);
|
|
7781
|
+
}
|
|
7782
|
+
if (displayInputTokens > 0) {
|
|
7783
|
+
const inputPrefix = this.isStreaming && this.streamingInputTokens > 0 ? "~" : "";
|
|
7784
|
+
parts.push(`${YELLOW2}\u2191${inputPrefix}${formatTokens(displayInputTokens)}${RESET3}`);
|
|
7443
7785
|
}
|
|
7444
|
-
const inputPrefix = this.isStreaming && this.streamingInputTokens > 0 ? "~" : "";
|
|
7445
|
-
parts.push(`${YELLOW2}\u2191 ${inputPrefix}${formatTokens(displayInputTokens)}${RESET2}`);
|
|
7446
7786
|
if (this.metrics.cachedTokens > 0) {
|
|
7447
|
-
parts.push(`${BLUE}\u293F
|
|
7787
|
+
parts.push(`${BLUE}\u293F${formatTokens(this.metrics.cachedTokens)}${RESET3}`);
|
|
7788
|
+
}
|
|
7789
|
+
if (displayOutputTokens > 0) {
|
|
7790
|
+
const outputPrefix = this.isStreaming ? "~" : "";
|
|
7791
|
+
parts.push(`${GREEN2}\u2193${outputPrefix}${formatTokens(displayOutputTokens)}${RESET3}`);
|
|
7792
|
+
}
|
|
7793
|
+
const earliestStart = this.getEarliestLLMCallStartTime();
|
|
7794
|
+
if (earliestStart !== null) {
|
|
7795
|
+
const elapsedSeconds = (Date.now() - earliestStart) / 1e3;
|
|
7796
|
+
const timeStr = elapsedSeconds % 1 === 0 ? `${elapsedSeconds}s` : `${elapsedSeconds.toFixed(1)}s`;
|
|
7797
|
+
parts.push(`${GRAY2}${timeStr}${RESET3}`);
|
|
7798
|
+
}
|
|
7799
|
+
if (this.metrics.cost > 0) {
|
|
7800
|
+
parts.push(`${CYAN2}$${formatCost(this.metrics.cost)}${RESET3}`);
|
|
7448
7801
|
}
|
|
7449
|
-
const outputPrefix = this.isStreaming ? "~" : "";
|
|
7450
|
-
parts.push(`${GREEN2}\u2193 ${outputPrefix}${formatTokens(displayOutputTokens)}${RESET2}`);
|
|
7451
|
-
parts.push(`${GRAY}${elapsed}s${RESET2}`);
|
|
7452
|
-
parts.push(`${CYAN2}$${formatCost(this.metrics.cost)}${RESET2}`);
|
|
7453
7802
|
if (this.selectionDebugCallback) {
|
|
7454
7803
|
const debug = this.selectionDebugCallback();
|
|
7455
7804
|
const debugStr = `sel:${debug.index}/${debug.total}`;
|
|
7456
7805
|
const typeStr = debug.nodeType ? ` [${debug.nodeType}]` : "";
|
|
7457
|
-
parts.push(`${
|
|
7806
|
+
parts.push(`${GRAY2}${debugStr}${typeStr}${RESET3}`);
|
|
7458
7807
|
}
|
|
7459
7808
|
if (this.rateLimitState?.isThrottling) {
|
|
7460
7809
|
const { triggeredBy } = this.rateLimitState;
|
|
7461
7810
|
if (triggeredBy?.daily) {
|
|
7462
|
-
parts.push(`${YELLOW2}\u23F8 Daily limit, resets midnight UTC${
|
|
7811
|
+
parts.push(`${YELLOW2}\u23F8 Daily limit, resets midnight UTC${RESET3}`);
|
|
7463
7812
|
} else {
|
|
7464
7813
|
const seconds = Math.ceil(this.rateLimitState.delayMs / 1e3);
|
|
7465
7814
|
const reason = triggeredBy?.rpm ? " (RPM)" : triggeredBy?.tpm ? " (TPM)" : "";
|
|
7466
|
-
parts.push(`${YELLOW2}\u23F8 Throttled ${seconds}s${reason}${
|
|
7815
|
+
parts.push(`${YELLOW2}\u23F8 Throttled ${seconds}s${reason}${RESET3}`);
|
|
7467
7816
|
}
|
|
7468
7817
|
}
|
|
7469
7818
|
if (this.retryState) {
|
|
7470
7819
|
const { attemptNumber, retriesLeft } = this.retryState;
|
|
7471
7820
|
const totalAttempts = attemptNumber + retriesLeft;
|
|
7472
|
-
parts.push(`${BLUE}\u{1F504} Retry ${attemptNumber}/${totalAttempts}${
|
|
7821
|
+
parts.push(`${BLUE}\u{1F504} Retry ${attemptNumber}/${totalAttempts}${RESET3}`);
|
|
7473
7822
|
}
|
|
7474
7823
|
if (this.activeLLMCalls.size > 0 || this.activeGadgets.size > 0) {
|
|
7475
7824
|
const spinner = SPINNER_FRAMES2[this.spinnerFrame];
|
|
7476
7825
|
if (this.activeLLMCalls.size > 0) {
|
|
7477
7826
|
const byModel = /* @__PURE__ */ new Map();
|
|
7478
|
-
for (const [label, model] of this.activeLLMCalls) {
|
|
7827
|
+
for (const [label, { model }] of this.activeLLMCalls) {
|
|
7479
7828
|
const shortModel = this.shortenModelName(model);
|
|
7480
7829
|
if (!byModel.has(shortModel)) byModel.set(shortModel, []);
|
|
7481
7830
|
byModel.get(shortModel)?.push(label);
|
|
@@ -7484,15 +7833,15 @@ var StatusBar = class {
|
|
|
7484
7833
|
for (const [model, labels] of byModel) {
|
|
7485
7834
|
llmParts.push(`${model} ${labels.join(", ")}`);
|
|
7486
7835
|
}
|
|
7487
|
-
parts.push(`${spinner} ${MAGENTA2}${llmParts.join(" | ")}${
|
|
7836
|
+
parts.push(`${spinner} ${MAGENTA2}${llmParts.join(" | ")}${RESET3}`);
|
|
7488
7837
|
}
|
|
7489
7838
|
if (this.activeGadgets.size > 0) {
|
|
7490
7839
|
const gadgetList = [...this.activeGadgets].slice(0, 3).join(", ");
|
|
7491
7840
|
const more = this.activeGadgets.size > 3 ? ` +${this.activeGadgets.size - 3}` : "";
|
|
7492
|
-
parts.push(`${CYAN2}\u23F5 ${gadgetList}${more}${
|
|
7841
|
+
parts.push(`${CYAN2}\u23F5 ${gadgetList}${more}${RESET3}`);
|
|
7493
7842
|
}
|
|
7494
7843
|
}
|
|
7495
|
-
this.statusBox.setContent(parts.join(
|
|
7844
|
+
this.statusBox.setContent(parts.join(" "));
|
|
7496
7845
|
if (immediate) {
|
|
7497
7846
|
this.renderNowCallback();
|
|
7498
7847
|
} else {
|
|
@@ -7538,7 +7887,12 @@ var TUIApp = class _TUIApp {
|
|
|
7538
7887
|
title: "llmist"
|
|
7539
7888
|
});
|
|
7540
7889
|
const { screen } = screenCtx;
|
|
7541
|
-
const
|
|
7890
|
+
const showHints = options.showHints ?? true;
|
|
7891
|
+
const layout = createBlockLayout(screen, showHints);
|
|
7892
|
+
let hintsBar = null;
|
|
7893
|
+
if (layout.hintsBar) {
|
|
7894
|
+
hintsBar = new HintsBar(layout.hintsBar, () => screenCtx.requestRender());
|
|
7895
|
+
}
|
|
7542
7896
|
const statusBar = new StatusBar(
|
|
7543
7897
|
layout.statusBar,
|
|
7544
7898
|
options.model,
|
|
@@ -7551,25 +7905,34 @@ var TUIApp = class _TUIApp {
|
|
|
7551
7905
|
layout.body,
|
|
7552
7906
|
screen,
|
|
7553
7907
|
() => screenCtx.requestRender(),
|
|
7554
|
-
() => screenCtx.renderNow()
|
|
7908
|
+
() => screenCtx.renderNow(),
|
|
7909
|
+
showHints
|
|
7555
7910
|
);
|
|
7556
7911
|
const blockRenderer = new BlockRenderer(
|
|
7557
7912
|
layout.body,
|
|
7558
7913
|
() => screenCtx.requestRender(),
|
|
7559
7914
|
() => screenCtx.renderNow()
|
|
7560
7915
|
);
|
|
7916
|
+
if (hintsBar) {
|
|
7917
|
+
blockRenderer.onHasContentChange((hasContent) => {
|
|
7918
|
+
hintsBar.setHasContent(hasContent);
|
|
7919
|
+
});
|
|
7920
|
+
}
|
|
7561
7921
|
const controller = new TUIController({
|
|
7562
7922
|
onFocusModeChange: (mode) => {
|
|
7563
7923
|
applyFocusMode(mode, layout, statusBar, inputHandler, screenCtx);
|
|
7924
|
+
hintsBar?.setFocusMode(mode);
|
|
7564
7925
|
},
|
|
7565
7926
|
onContentFilterModeChange: (mode) => {
|
|
7566
7927
|
applyContentFilterMode(mode, blockRenderer, statusBar, screenCtx);
|
|
7928
|
+
hintsBar?.setContentFilterMode(mode);
|
|
7567
7929
|
}
|
|
7568
7930
|
});
|
|
7569
7931
|
const modalManager = new ModalManager();
|
|
7570
7932
|
const keyboardManager = new KeyboardManager({
|
|
7571
7933
|
screen,
|
|
7572
7934
|
getFocusMode: () => controller.getFocusMode(),
|
|
7935
|
+
getContentFilterMode: () => controller.getContentFilterMode(),
|
|
7573
7936
|
isWaitingForREPLPrompt: () => inputHandler.isWaitingForREPLPrompt(),
|
|
7574
7937
|
hasPendingInput: () => inputHandler.hasPendingInput(),
|
|
7575
7938
|
isBlockExpanded: () => blockRenderer.getSelectedBlock()?.expanded ?? false,
|
|
@@ -7717,13 +8080,11 @@ var TUIApp = class _TUIApp {
|
|
|
7717
8080
|
}
|
|
7718
8081
|
/**
|
|
7719
8082
|
* Wait for user to enter a new prompt (REPL mode).
|
|
7720
|
-
* Stays in
|
|
7721
|
-
*
|
|
8083
|
+
* Stays in input mode after submission - user can watch output and type next message.
|
|
8084
|
+
* User can Ctrl+B to browse if they want to navigate blocks.
|
|
7722
8085
|
*/
|
|
7723
8086
|
async waitForPrompt() {
|
|
7724
|
-
|
|
7725
|
-
this.controller.setFocusMode("browse");
|
|
7726
|
-
return result;
|
|
8087
|
+
return this.inputHandler.waitForPrompt();
|
|
7727
8088
|
}
|
|
7728
8089
|
/**
|
|
7729
8090
|
* Enter the pending REPL prompt state without blocking.
|
|
@@ -7942,17 +8303,13 @@ var TUIApp = class _TUIApp {
|
|
|
7942
8303
|
};
|
|
7943
8304
|
function applyFocusMode(mode, layout, statusBar, inputHandler, screenCtx) {
|
|
7944
8305
|
statusBar.setFocusMode(mode);
|
|
7945
|
-
if (mode === "input") {
|
|
7946
|
-
layout.body.height = "100%-2";
|
|
7947
|
-
} else {
|
|
7948
|
-
layout.body.height = "100%-1";
|
|
7949
|
-
}
|
|
7950
|
-
screenCtx.renderNow();
|
|
7951
8306
|
if (mode === "input") {
|
|
7952
8307
|
inputHandler.activate();
|
|
7953
8308
|
} else {
|
|
7954
8309
|
inputHandler.deactivate();
|
|
8310
|
+
layout.body.focus();
|
|
7955
8311
|
}
|
|
8312
|
+
screenCtx.renderNow();
|
|
7956
8313
|
}
|
|
7957
8314
|
function applyContentFilterMode(mode, blockRenderer, statusBar, screenCtx) {
|
|
7958
8315
|
blockRenderer.setContentFilterMode(mode);
|
|
@@ -7997,6 +8354,14 @@ function handleKeyAction(action, controller, blockRenderer, statusBar, screenCtx
|
|
|
7997
8354
|
screenCtx.renderNow();
|
|
7998
8355
|
break;
|
|
7999
8356
|
}
|
|
8357
|
+
case "scroll_line": {
|
|
8358
|
+
const body = layout.body;
|
|
8359
|
+
if (!body.scroll) return;
|
|
8360
|
+
body.scroll(action.direction);
|
|
8361
|
+
blockRenderer.handleUserScroll();
|
|
8362
|
+
screenCtx.renderNow();
|
|
8363
|
+
break;
|
|
8364
|
+
}
|
|
8000
8365
|
case "navigation":
|
|
8001
8366
|
switch (action.action) {
|
|
8002
8367
|
case "select_next":
|
|
@@ -8082,7 +8447,8 @@ async function executeAgent(promptArg, options, env, commandName) {
|
|
|
8082
8447
|
tui = await TUIApp.create({
|
|
8083
8448
|
model: options.model,
|
|
8084
8449
|
stdin: env.stdin,
|
|
8085
|
-
stdout: env.stdout
|
|
8450
|
+
stdout: env.stdout,
|
|
8451
|
+
showHints: options.showHints
|
|
8086
8452
|
});
|
|
8087
8453
|
try {
|
|
8088
8454
|
const fullConfig = loadConfig();
|
|
@@ -8471,7 +8837,8 @@ function registerAgentCommand(program, env, config, globalSubagents, globalRateL
|
|
|
8471
8837
|
globalRateLimits,
|
|
8472
8838
|
globalRetry,
|
|
8473
8839
|
profileRateLimits: config?.["rate-limits"],
|
|
8474
|
-
profileRetry: config?.retry
|
|
8840
|
+
profileRetry: config?.retry,
|
|
8841
|
+
showHints: config?.["show-hints"]
|
|
8475
8842
|
};
|
|
8476
8843
|
return executeAgent(prompt, mergedOptions, env, "agent");
|
|
8477
8844
|
}, env)
|
|
@@ -8696,7 +9063,7 @@ System Prompt (${chars.toLocaleString()} chars, ${lines} lines):
|
|
|
8696
9063
|
}
|
|
8697
9064
|
|
|
8698
9065
|
// src/environment.ts
|
|
8699
|
-
import { join as
|
|
9066
|
+
import { join as join4 } from "path";
|
|
8700
9067
|
import readline from "readline";
|
|
8701
9068
|
import chalk4 from "chalk";
|
|
8702
9069
|
import { createLogger, LLMist } from "llmist";
|
|
@@ -8719,7 +9086,7 @@ function createLoggerFactory(config, sessionLogDir) {
|
|
|
8719
9086
|
}
|
|
8720
9087
|
}
|
|
8721
9088
|
if (sessionLogDir) {
|
|
8722
|
-
const logFile =
|
|
9089
|
+
const logFile = join4(sessionLogDir, "session.log.jsonl");
|
|
8723
9090
|
const originalLogFile = process.env.LLMIST_LOG_FILE;
|
|
8724
9091
|
process.env.LLMIST_LOG_FILE = logFile;
|
|
8725
9092
|
const logger = createLogger(options);
|
|
@@ -9265,7 +9632,7 @@ function registerGadgetCommand(program, env) {
|
|
|
9265
9632
|
}
|
|
9266
9633
|
|
|
9267
9634
|
// src/image-command.ts
|
|
9268
|
-
import { writeFileSync as
|
|
9635
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
9269
9636
|
var DEFAULT_IMAGE_MODEL = "dall-e-3";
|
|
9270
9637
|
async function executeImage(promptArg, options, env) {
|
|
9271
9638
|
const prompt = await resolvePrompt(promptArg, env);
|
|
@@ -9289,7 +9656,7 @@ async function executeImage(promptArg, options, env) {
|
|
|
9289
9656
|
const imageData = result.images[0];
|
|
9290
9657
|
if (imageData.b64Json) {
|
|
9291
9658
|
const buffer = Buffer.from(imageData.b64Json, "base64");
|
|
9292
|
-
|
|
9659
|
+
writeFileSync3(options.output, buffer);
|
|
9293
9660
|
if (!options.quiet) {
|
|
9294
9661
|
env.stderr.write(`${SUMMARY_PREFIX} Image saved to ${options.output}
|
|
9295
9662
|
`);
|
|
@@ -9328,7 +9695,7 @@ function registerImageCommand(program, env, config) {
|
|
|
9328
9695
|
}
|
|
9329
9696
|
|
|
9330
9697
|
// src/init-command.ts
|
|
9331
|
-
import { existsSync as existsSync3, mkdirSync, writeFileSync as
|
|
9698
|
+
import { existsSync as existsSync3, mkdirSync, writeFileSync as writeFileSync4 } from "fs";
|
|
9332
9699
|
import { dirname as dirname2 } from "path";
|
|
9333
9700
|
var STARTER_CONFIG = `# ~/.llmist/cli.toml
|
|
9334
9701
|
# llmist CLI configuration file
|
|
@@ -9399,7 +9766,7 @@ async function executeInit(_options, env) {
|
|
|
9399
9766
|
if (!existsSync3(configDir)) {
|
|
9400
9767
|
mkdirSync(configDir, { recursive: true });
|
|
9401
9768
|
}
|
|
9402
|
-
|
|
9769
|
+
writeFileSync4(configPath, STARTER_CONFIG, "utf-8");
|
|
9403
9770
|
env.stderr.write(`Created ${configPath}
|
|
9404
9771
|
`);
|
|
9405
9772
|
env.stderr.write("\n");
|
|
@@ -9831,7 +10198,7 @@ function registerModelsCommand(program, env) {
|
|
|
9831
10198
|
import { existsSync as existsSync4 } from "fs";
|
|
9832
10199
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
9833
10200
|
import { homedir as homedir3 } from "os";
|
|
9834
|
-
import { join as
|
|
10201
|
+
import { join as join5 } from "path";
|
|
9835
10202
|
|
|
9836
10203
|
// src/session-names.ts
|
|
9837
10204
|
var ADJECTIVES = [
|
|
@@ -9966,16 +10333,16 @@ function generateSessionName() {
|
|
|
9966
10333
|
|
|
9967
10334
|
// src/session.ts
|
|
9968
10335
|
var currentSession;
|
|
9969
|
-
var SESSION_LOGS_BASE =
|
|
10336
|
+
var SESSION_LOGS_BASE = join5(homedir3(), ".llmist", "logs");
|
|
9970
10337
|
function findUniqueName(baseName) {
|
|
9971
|
-
const baseDir =
|
|
10338
|
+
const baseDir = join5(SESSION_LOGS_BASE, baseName);
|
|
9972
10339
|
if (!existsSync4(baseDir)) {
|
|
9973
10340
|
return baseName;
|
|
9974
10341
|
}
|
|
9975
10342
|
let suffix = 2;
|
|
9976
10343
|
while (suffix < 1e3) {
|
|
9977
10344
|
const name = `${baseName}-${suffix}`;
|
|
9978
|
-
const dir =
|
|
10345
|
+
const dir = join5(SESSION_LOGS_BASE, name);
|
|
9979
10346
|
if (!existsSync4(dir)) {
|
|
9980
10347
|
return name;
|
|
9981
10348
|
}
|
|
@@ -9989,14 +10356,14 @@ async function initSession() {
|
|
|
9989
10356
|
}
|
|
9990
10357
|
const baseName = generateSessionName();
|
|
9991
10358
|
const name = findUniqueName(baseName);
|
|
9992
|
-
const logDir =
|
|
10359
|
+
const logDir = join5(SESSION_LOGS_BASE, name);
|
|
9993
10360
|
await mkdir2(logDir, { recursive: true });
|
|
9994
10361
|
currentSession = { name, logDir };
|
|
9995
10362
|
return currentSession;
|
|
9996
10363
|
}
|
|
9997
10364
|
|
|
9998
10365
|
// src/speech-command.ts
|
|
9999
|
-
import { writeFileSync as
|
|
10366
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
10000
10367
|
var DEFAULT_SPEECH_MODEL = "tts-1";
|
|
10001
10368
|
var DEFAULT_VOICE = "nova";
|
|
10002
10369
|
async function executeSpeech(textArg, options, env) {
|
|
@@ -10019,7 +10386,7 @@ async function executeSpeech(textArg, options, env) {
|
|
|
10019
10386
|
});
|
|
10020
10387
|
const audioBuffer = Buffer.from(result.audio);
|
|
10021
10388
|
if (options.output) {
|
|
10022
|
-
|
|
10389
|
+
writeFileSync5(options.output, audioBuffer);
|
|
10023
10390
|
if (!options.quiet) {
|
|
10024
10391
|
env.stderr.write(`${SUMMARY_PREFIX} Audio saved to ${options.output}
|
|
10025
10392
|
`);
|