fluxflow-cli 1.3.6 → 1.4.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/UI_FEATURES.md +15 -0
- package/dist/fluxflow.js +74 -20
- package/package.json +1 -1
package/UI_FEATURES.md
CHANGED
|
@@ -35,6 +35,21 @@ Flux Flow separates the model's "internal monologue" (reasoning) from its final
|
|
|
35
35
|
- **Show/Hide Thinking**: You can toggle the visibility of the thinking process using `/thinking show/hide`.
|
|
36
36
|
- When **Hidden**, the agent doesn't just disappear; it provides a "minimalist" view showing only the core **Headings** and **Action Steps** (bolded lines) from its reasoning. This keeps you informed of its current "step" without cluttering the screen with detailed internal monologue.
|
|
37
37
|
|
|
38
|
+
## ⚡ Interactive Sub-Terminal
|
|
39
|
+
|
|
40
|
+
Flux Flow features a high-fidelity, interactive sub-terminal that allows you to engage with commands spawned by the agent in real-time.
|
|
41
|
+
|
|
42
|
+
- **Live Interaction**: When an agent executes a command (like `npm init`, `git commit`, or a custom script), the terminal output appears in a dedicated box.
|
|
43
|
+
- **Focus Toggling (`TAB`)**: While a terminal is live, you can press **`TAB`** to shift your keyboard focus from the chat prompt directly into the terminal.
|
|
44
|
+
- **Visual Feedback**:
|
|
45
|
+
- **Yellow Glow**: When the terminal is focused, its border changes to a "Double Yellow" style to indicate it is capturing input.
|
|
46
|
+
- **Status Indicator**: The footer will change from `● LIVE` to `▶ TERMINAL FOCUSED`.
|
|
47
|
+
- **Cross-Platform Compatibility**: The input bridge automatically detects your OS and sends the correct line endings (`\r\n` for Windows, `\n` for Unix), ensuring that interactive prompts like `y/n` work seamlessly across environments.
|
|
48
|
+
- **Input Mirroring**: Your keystrokes are mirrored in the terminal box in real-time, providing an IDE-grade responsive feel.
|
|
49
|
+
|
|
50
|
+
> [!TIP]
|
|
51
|
+
> Use the Interactive Sub-Terminal to handle authentication prompts, confirmation dialogs, or to manually steering a long-running process without leaving the Flux Flow interface.
|
|
52
|
+
|
|
38
53
|
## 🛡️ Human-in-the-Loop (HITL) Verification
|
|
39
54
|
|
|
40
55
|
Security and safety are paramount when an AI has access to your file system and terminal. Flux Flow implements several layers of verification:
|
package/dist/fluxflow.js
CHANGED
|
@@ -15,9 +15,9 @@ import { Box, Text } from "ink";
|
|
|
15
15
|
var TerminalBox;
|
|
16
16
|
var init_TerminalBox = __esm({
|
|
17
17
|
"src/components/TerminalBox.jsx"() {
|
|
18
|
-
TerminalBox = React.memo(({ command, output, completed = false }) => {
|
|
18
|
+
TerminalBox = React.memo(({ command, output, completed = false, isFocused = false }) => {
|
|
19
19
|
const cleanOutput = (output || "").replace(/\r/g, "").trim();
|
|
20
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: completed ? "#334155" : "cyan", paddingX: 2, paddingY: completed ? 0 : 1, width: "100%" }, /* @__PURE__ */ React.createElement(Box, {
|
|
20
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", borderStyle: isFocused ? "double" : "round", borderColor: completed ? "#334155" : isFocused ? "yellow" : "cyan", paddingX: 2, paddingY: completed ? 0 : 1, width: "100%" }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : isFocused ? "yellow" : "cyan", bold: true }, completed ? "\u{1F3C1} FINISHED:" : "\u26A1 EXECUTING:", " "), /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "white" }, command)), cleanOutput ? /* @__PURE__ */ React.createElement(Box, { marginTop: completed ? 0 : 1, backgroundColor: "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "green", wrap: "anywhere" }, cleanOutput)) : !completed && /* @__PURE__ */ React.createElement(Box, { marginTop: 1, backgroundColor: "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", italic: true }, "Waiting for output...")), /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", marginTop: 1 }, !completed ? /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true, italic: true }, "Double-press ESC to terminate if hanging.") : /* @__PURE__ */ React.createElement(Box, null), /* @__PURE__ */ React.createElement(Text, { color: completed ? "#475569" : isFocused ? "yellow" : "cyan", bold: true }, completed ? "\u25CF ARCHIVED" : isFocused ? "\u25B6 TERMINAL FOCUSED" : "\u25CF LIVE (Press TAB to focus)")));
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
});
|
|
@@ -57,16 +57,22 @@ var init_ChatLayout = __esm({
|
|
|
57
57
|
}
|
|
58
58
|
return parts.join("").replace(/^\[TOOL_RESULT\]:\s*/gi, "").split("\n").filter((line) => !line.trim().startsWith("SUCCESS:") && !line.trim().startsWith("ERROR:")).join("\n").replace(/\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").replace(/\n\s*turn\s*:\s*(continue|finish)\s*$/gi, "").replace(/\n\nResponded on .*/g, "").replace(/\n\n\[Prompted on: .*\]/g, "").replace(/(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi, "\u2192").replace(/(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)/gi, "\u2190").replace(/(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)/gi, "\u2191").replace(/(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)/gi, "\u2193").replace(/(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi, "\u2194").trim();
|
|
59
59
|
};
|
|
60
|
-
formatThinkText = (cleaned) => {
|
|
60
|
+
formatThinkText = (cleaned, columns = 80) => {
|
|
61
61
|
if (!cleaned) return null;
|
|
62
|
-
const lines = cleaned.split("\n")
|
|
62
|
+
const lines = cleaned.split("\n");
|
|
63
|
+
const availableWidth = columns - 10;
|
|
63
64
|
return lines.map((line, i) => {
|
|
64
65
|
const trimmed = line.trim();
|
|
66
|
+
if (!trimmed) return /* @__PURE__ */ React2.createElement(Box2, { key: i, height: 1 });
|
|
65
67
|
if (trimmed.startsWith("**") && trimmed.endsWith("**") || trimmed.startsWith("#")) {
|
|
66
|
-
|
|
68
|
+
const hText = trimmed.replace(/\*|#/g, "").trim();
|
|
69
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginTop: i === 0 ? 0 : 1, marginBottom: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "\u25B8 ", wrapText(hText, availableWidth - 2)));
|
|
67
70
|
}
|
|
68
71
|
const isBullet = trimmed.startsWith("*");
|
|
69
|
-
|
|
72
|
+
const bulletPrefix = isBullet ? "\u2022 " : "";
|
|
73
|
+
const cleanText = trimmed.replace(/^\*|\s\*/g, "").trim();
|
|
74
|
+
const wrapped = wrapText(cleanText, availableWidth - (isBullet ? 2 : 0));
|
|
75
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginLeft: isBullet ? 2 : 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { italic: true, color: "gray" }, bulletPrefix, wrapped.split("\n").join("\n" + " ".repeat(bulletPrefix.length))));
|
|
70
76
|
});
|
|
71
77
|
};
|
|
72
78
|
InlineMarkdown = React2.memo(({ text, color }) => {
|
|
@@ -307,7 +313,7 @@ var init_ChatLayout = __esm({
|
|
|
307
313
|
finalContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\n/g, "\n").replace(/\\$/, ""),
|
|
308
314
|
columns - 6
|
|
309
315
|
).split("\n").map((line, lineIdx) => /* @__PURE__ */ React2.createElement(Box2, { key: lineIdx, flexDirection: "row", width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexShrink: 0, width: 2 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, lineIdx === 0 ? "\u276F" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: msg.color || "white", wrap: "anywhere" }, line))))
|
|
310
|
-
) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Thinking..."), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, flexDirection: "column", width: "100%" }, formatThinkText(finalContent))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: finalContent, columns }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))));
|
|
316
|
+
) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Thinking..."), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, flexDirection: "column", width: "100%" }, formatThinkText(finalContent, columns))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: finalContent, columns }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))));
|
|
311
317
|
});
|
|
312
318
|
ChatLayout = React2.memo(({ messages, showFullThinking, columns = 80 }) => {
|
|
313
319
|
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, messages.map((msg, idx) => /* @__PURE__ */ React2.createElement(
|
|
@@ -623,10 +629,10 @@ var thinking_prompts_default;
|
|
|
623
629
|
var init_thinking_prompts = __esm({
|
|
624
630
|
"src/data/thinking_prompts.json"() {
|
|
625
631
|
thinking_prompts_default = {
|
|
626
|
-
Max: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MAX\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading
|
|
627
|
-
High: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: HIGH\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading
|
|
628
|
-
Medium: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MEDIUM\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading
|
|
629
|
-
Minimal: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: LOW\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading
|
|
632
|
+
Max: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MAX\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 10**, **MAX 15**. EACH HEADING SHOULD HAVE MIN 12, MAX 16 SENTENCES OF INTERNAL MONOLOGUE. EXPLORE EDGE CASES & NUANCES. Dynamic Thinking preferred based on query.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\nConverge Thinking & Avoid running into thinking Loops. YOU MUST NOT EXCEED ALLOTTED THINKING BUDGET. COMPLETE YOUR THINKING WITHIN LIMITS AND START YOUR RESPONSE. DEFINE YOUR DYNAMIC THINKING BUDGET BASED ON THE QUERY COMPLEXITY. DO NOT OVERTHINK SIMPLE ONES.\n-- END THINKING INSTRUCTIONS --",
|
|
633
|
+
High: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: HIGH\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 7**, **MAX 10**. EACH HEADING SHOULD HAVE MIN 8, MAX 12 SENTENCES OF INTERNAL MONOLOGUE. EXPLORE EDGE CASES & THINK LONGER before responding. Dynamic Thinking preferred based on query.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\nConverge Thinking & Avoid running into thinking Loops. YOU MUST NOT EXCEED ALLOTTED THINKING BUDGET. COMPLETE YOUR THINKING WITHIN LIMITS AND START YOUR RESPONSE. DEFINE YOUR DYNAMIC THINKING BUDGET BASED ON THE QUERY COMPLEXITY. DO NOT OVERTHINK SIMPLE ONES.\n-- END THINKING INSTRUCTIONS --",
|
|
634
|
+
Medium: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MEDIUM\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 3**, **MAX 7**. EACH HEADING SHOULD HAVE MIN 4, MAX 8 SENTENCES OF INTERNAL MONOLOGUE. THINK LONGER FOR COMPLEX QUERIES. Dynamic Thinking preferred based on query.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\nConverge Thinking & Avoid running into thinking Loops. YOU MUST NOT EXCEED ALLOTTED THINKING BUDGET. COMPLETE YOUR THINKING WITHIN LIMITS AND START YOUR RESPONSE. DEFINE YOUR DYNAMIC THINKING BUDGET BASED ON THE QUERY COMPLEXITY. DO NOT OVERTHINK SIMPLE ONES.\n-- END THINKING INSTRUCTIONS --",
|
|
635
|
+
Minimal: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: LOW\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\n\\n\\n\n**Heading**\n\\n\\n\nSelf MONOLOGUE\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 0**, **MAX 2**. EACH HEADING SHOULD HAVE MIN 1, MAX 3 SENTENCES OF INTERNAL MONOLOGUE. No Thinking is preferred if query is simple.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\nConverge Thinking & Avoid running into thinking Loops. YOU MUST NOT EXCEED ALLOTTED THINKING BUDGET. COMPLETE YOUR THINKING WITHIN LIMITS AND START YOUR RESPONSE. DEFINE YOUR DYNAMIC THINKING BUDGET BASED ON THE QUERY COMPLEXITY. DO NOT OVERTHINK SIMPLE ONES.\n-- END THINKING INSTRUCTIONS --"
|
|
630
636
|
};
|
|
631
637
|
}
|
|
632
638
|
});
|
|
@@ -699,8 +705,9 @@ Every ${isMemoryEnabled ? "Prompt, Responses & Memories" : "Prompt & Responses"}
|
|
|
699
705
|
-- START FORMATTING RULES --
|
|
700
706
|
- Structure responses VISUALLY pleasing, easy to read, and beautiful.
|
|
701
707
|
- USE GFM Markdown HEAVILY.
|
|
702
|
-
- Use GFM tables for structured data to keep the terminal view organized.
|
|
708
|
+
- Use GFM tables for structured data to keep the terminal view organized. KEEP SENTENCES IN TABLE **SHORT & CONCISE**. AND MAX 3 COLUMNS. DO NOT OVERUSE TABLES.
|
|
703
709
|
- **CRITICAL**: NEVER USE LaTeX IN TERMINAL RESPONSES (exception: file content).
|
|
710
|
+
- Keep Poems & Literature in Code Block.
|
|
704
711
|
- Use emojis & Kaomojis.
|
|
705
712
|
-- END FORMATTING RULES --
|
|
706
713
|
|
|
@@ -780,10 +787,11 @@ var init_history = __esm({
|
|
|
780
787
|
return withLock(async () => {
|
|
781
788
|
const history = await loadHistory();
|
|
782
789
|
const existingChat = history[id];
|
|
790
|
+
const persistentMessages = (messages || []).filter((m) => !m.isUpdateNotification && !m.isMeta);
|
|
783
791
|
const finalName = name || (existingChat ? existingChat.name : `Session ${id.slice(-6)}`);
|
|
784
792
|
history[id] = {
|
|
785
793
|
name: finalName,
|
|
786
|
-
messages,
|
|
794
|
+
messages: persistentMessages,
|
|
787
795
|
updatedAt: Date.now()
|
|
788
796
|
};
|
|
789
797
|
await fs3.ensureDir(path4.dirname(HISTORY_FILE));
|
|
@@ -1375,16 +1383,40 @@ var init_update_file = __esm({
|
|
|
1375
1383
|
|
|
1376
1384
|
// src/tools/exec_command.js
|
|
1377
1385
|
import { spawn } from "child_process";
|
|
1378
|
-
var exec_command;
|
|
1386
|
+
var activeChildProcess, writeToActiveCommand, exec_command;
|
|
1379
1387
|
var init_exec_command = __esm({
|
|
1380
1388
|
"src/tools/exec_command.js"() {
|
|
1381
1389
|
init_arg_parser();
|
|
1390
|
+
activeChildProcess = null;
|
|
1391
|
+
writeToActiveCommand = (data) => {
|
|
1392
|
+
try {
|
|
1393
|
+
if (activeChildProcess && activeChildProcess.stdin && activeChildProcess.stdin.writable) {
|
|
1394
|
+
activeChildProcess.stdin.write(data);
|
|
1395
|
+
}
|
|
1396
|
+
} catch (err) {
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1382
1399
|
exec_command = async (args, options = {}) => {
|
|
1383
1400
|
const { command } = parseArgs(args);
|
|
1384
1401
|
const { onChunk } = options;
|
|
1385
1402
|
if (!command) return 'ERROR: Missing "command" argument for exec_command.';
|
|
1386
1403
|
return new Promise((resolve) => {
|
|
1387
|
-
const child = spawn(command, {
|
|
1404
|
+
const child = spawn(command, {
|
|
1405
|
+
shell: true,
|
|
1406
|
+
cwd: process.cwd(),
|
|
1407
|
+
env: {
|
|
1408
|
+
...process.env,
|
|
1409
|
+
CI: "false",
|
|
1410
|
+
TERM: "xterm-256color",
|
|
1411
|
+
FORCE_COLOR: "1"
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
activeChildProcess = child;
|
|
1415
|
+
if (child.stdin) {
|
|
1416
|
+
child.stdin.on("error", () => {
|
|
1417
|
+
activeChildProcess = null;
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1388
1420
|
let stdout = "";
|
|
1389
1421
|
let stderr = "";
|
|
1390
1422
|
child.stdout.on("data", (data) => {
|
|
@@ -1398,6 +1430,7 @@ var init_exec_command = __esm({
|
|
|
1398
1430
|
if (onChunk) onChunk(chunk);
|
|
1399
1431
|
});
|
|
1400
1432
|
child.on("close", (code) => {
|
|
1433
|
+
activeChildProcess = null;
|
|
1401
1434
|
const result = [];
|
|
1402
1435
|
if (stdout) result.push(`STDOUT:
|
|
1403
1436
|
${stdout}`);
|
|
@@ -1416,6 +1449,7 @@ ${finalOutput}`);
|
|
|
1416
1449
|
}
|
|
1417
1450
|
});
|
|
1418
1451
|
child.on("error", (err) => {
|
|
1452
|
+
activeChildProcess = null;
|
|
1419
1453
|
resolve(`ERROR: Failed to start command [${command}]: ${err.message}`);
|
|
1420
1454
|
});
|
|
1421
1455
|
});
|
|
@@ -1712,7 +1746,6 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1712
1746
|
} else if (retryCount > 0) {
|
|
1713
1747
|
yield { type: "model_update", content: null };
|
|
1714
1748
|
}
|
|
1715
|
-
fs12.writeFileSync("contents.json", JSON.stringify(contents));
|
|
1716
1749
|
stream = await client.models.generateContentStream({
|
|
1717
1750
|
model: targetModel,
|
|
1718
1751
|
contents,
|
|
@@ -2346,6 +2379,7 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2346
2379
|
const [chatId, setChatId] = useState6(generateChatId());
|
|
2347
2380
|
const [activeCommand, setActiveCommand] = useState6(null);
|
|
2348
2381
|
const [execOutput, setExecOutput] = useState6("");
|
|
2382
|
+
const [isTerminalFocused, setIsTerminalFocused] = useState6(false);
|
|
2349
2383
|
const terminalEnv = useMemo(() => {
|
|
2350
2384
|
const isIDE = process.env.TERM_PROGRAM === "vscode" || !!process.env.VSC_TERMINAL_URL || !!process.env.INTELLIJ_TERMINAL_COMMAND_BLOCKS;
|
|
2351
2385
|
return {
|
|
@@ -2421,6 +2455,24 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2421
2455
|
}, 0);
|
|
2422
2456
|
const maxLines = Math.max(1, wrappedLinesCount);
|
|
2423
2457
|
useInput4((inputText, key) => {
|
|
2458
|
+
if (key.tab && activeCommand) {
|
|
2459
|
+
setIsTerminalFocused((prev) => !prev);
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
if (isTerminalFocused && activeCommand) {
|
|
2463
|
+
if (key.return) {
|
|
2464
|
+
const isWin = process.platform === "win32";
|
|
2465
|
+
writeToActiveCommand(isWin ? "\r\n" : "\n");
|
|
2466
|
+
setExecOutput((prev) => prev + "\n");
|
|
2467
|
+
} else if (key.backspace || key.delete) {
|
|
2468
|
+
writeToActiveCommand("\b \b");
|
|
2469
|
+
setExecOutput((prev) => prev.slice(0, -1));
|
|
2470
|
+
} else if (inputText) {
|
|
2471
|
+
writeToActiveCommand(inputText);
|
|
2472
|
+
setExecOutput((prev) => prev + inputText);
|
|
2473
|
+
}
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2424
2476
|
if (maxLines > 2 && !isExpanded && activeView === "chat") {
|
|
2425
2477
|
if (key.backspace || key.delete) {
|
|
2426
2478
|
setInput("");
|
|
@@ -2874,6 +2926,7 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2874
2926
|
return [...prev, { id: "term-" + Date.now(), role: "system", text: finalStatus, isTerminalRecord: true }];
|
|
2875
2927
|
});
|
|
2876
2928
|
setActiveCommand(null);
|
|
2929
|
+
setIsTerminalFocused(false);
|
|
2877
2930
|
setExecOutput("");
|
|
2878
2931
|
},
|
|
2879
2932
|
onToolApproval: async (tool, args) => {
|
|
@@ -3623,15 +3676,15 @@ Selection: ${val}`,
|
|
|
3623
3676
|
newline: (key) => key.return && key.shift || key.return && key.ctrl || key.return && key.leftAlt || key.return && key.rightAlt
|
|
3624
3677
|
}
|
|
3625
3678
|
}
|
|
3626
|
-
)))) : /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "row", width: "100%", paddingY: 0 }, /* @__PURE__ */ React10.createElement(Box10, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u276F ")), /* @__PURE__ */ React10.createElement(Box10, { flexGrow: 1 }, /* @__PURE__ */ React10.createElement(Box10, { flexGrow: 1, position: "relative" }, input === "" && !isProcessing && /* @__PURE__ */ React10.createElement(Box10, { position: "absolute", paddingLeft: 0 }, /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, escPressed ? " Press ESC again to cancel the request." : ` Type /cmd or message... (${terminalEnv.shortcut} for newline)`)), /* @__PURE__ */ React10.createElement(
|
|
3679
|
+
)))) : /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "row", width: "100%", paddingY: 0 }, /* @__PURE__ */ React10.createElement(Box10, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "\u276F ")), /* @__PURE__ */ React10.createElement(Box10, { flexGrow: 1 }, /* @__PURE__ */ React10.createElement(Box10, { flexGrow: 1, position: "relative" }, input === "" && !isProcessing && /* @__PURE__ */ React10.createElement(Box10, { position: "absolute", paddingLeft: 0 }, activeCommand && !isTerminalFocused ? /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, " Press TAB to interact with terminal...") : activeCommand && isTerminalFocused ? /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true }, " [ TERMINAL FOCUSED ] Type to interact, press TAB to exit...") : /* @__PURE__ */ React10.createElement(Text10, { color: "gray", dimColor: true }, escPressed ? " Press ESC again to cancel the request." : ` Type /cmd or message... (${terminalEnv.shortcut} for newline)`)), /* @__PURE__ */ React10.createElement(
|
|
3627
3680
|
MultilineInput,
|
|
3628
3681
|
{
|
|
3682
|
+
focus: !isTerminalFocused,
|
|
3629
3683
|
value: input,
|
|
3630
3684
|
onChange: (val) => {
|
|
3631
3685
|
const cleanVal = val.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\s*\n/g, "\n");
|
|
3632
3686
|
setInput(cleanVal);
|
|
3633
3687
|
},
|
|
3634
|
-
placeholder: escPressed ? " Press ESC again to cancel the request." : ` Type /cmd or message... (${terminalEnv.shortcut} for newline)`,
|
|
3635
3688
|
onSubmit: handleSubmit,
|
|
3636
3689
|
maxRows: 3,
|
|
3637
3690
|
keyBindings: {
|
|
@@ -3649,7 +3702,7 @@ Selection: ${val}`,
|
|
|
3649
3702
|
showFullThinking,
|
|
3650
3703
|
columns: stdout?.columns || 80
|
|
3651
3704
|
}
|
|
3652
|
-
), activeCommand && /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(TerminalBox, { command: activeCommand, output: execOutput }))), isInitializing ? /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React10.createElement(Text10, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "bold", borderColor: "yellow", padding: 1, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true }, "\u{1F511} API KEY REQUIRED"), /* @__PURE__ */ React10.createElement(Text10, null, "Please enter your Gemini API Key to initialize the agent's brain."), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "\u276F "), /* @__PURE__ */ React10.createElement(
|
|
3705
|
+
), activeCommand && /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(TerminalBox, { command: activeCommand, output: execOutput, isFocused: isTerminalFocused }))), isInitializing ? /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React10.createElement(Text10, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "bold", borderColor: "yellow", padding: 1, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true }, "\u{1F511} API KEY REQUIRED"), /* @__PURE__ */ React10.createElement(Text10, null, "Please enter your Gemini API Key to initialize the agent's brain."), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "\u276F "), /* @__PURE__ */ React10.createElement(
|
|
3653
3706
|
TextInput3,
|
|
3654
3707
|
{
|
|
3655
3708
|
value: tempKey,
|
|
@@ -3715,9 +3768,10 @@ var init_app = __esm({
|
|
|
3715
3768
|
init_arg_parser();
|
|
3716
3769
|
init_paths();
|
|
3717
3770
|
init_terminal();
|
|
3771
|
+
init_exec_command();
|
|
3718
3772
|
SESSION_START_TIME = Date.now();
|
|
3719
3773
|
CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
|
|
3720
|
-
versionFluxflow = "1.
|
|
3774
|
+
versionFluxflow = "1.4.0";
|
|
3721
3775
|
updatedOn = "2026-04-29";
|
|
3722
3776
|
ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION"), /* @__PURE__ */ React10.createElement(Text10, { marginTop: 1 }, "The agent already finished the task (turn: finish) before your hint was consumed."), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1, backgroundColor: "#222", paddingX: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(
|
|
3723
3777
|
CommandMenu,
|