fluxflow-cli 1.3.6 → 1.4.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/UI_FEATURES.md +15 -0
- package/dist/fluxflow.js +86 -22
- 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 7**, **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 5**, **MAX 7**. 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 5**. 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,49 @@ 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, terminateActiveCommand, 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
|
+
};
|
|
1399
|
+
terminateActiveCommand = () => {
|
|
1400
|
+
if (activeChildProcess) {
|
|
1401
|
+
try {
|
|
1402
|
+
activeChildProcess.kill("SIGKILL");
|
|
1403
|
+
} catch (err) {
|
|
1404
|
+
}
|
|
1405
|
+
activeChildProcess = null;
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1382
1408
|
exec_command = async (args, options = {}) => {
|
|
1383
1409
|
const { command } = parseArgs(args);
|
|
1384
1410
|
const { onChunk } = options;
|
|
1385
1411
|
if (!command) return 'ERROR: Missing "command" argument for exec_command.';
|
|
1386
1412
|
return new Promise((resolve) => {
|
|
1387
|
-
const child = spawn(command, {
|
|
1413
|
+
const child = spawn(command, {
|
|
1414
|
+
shell: true,
|
|
1415
|
+
cwd: process.cwd(),
|
|
1416
|
+
env: {
|
|
1417
|
+
...process.env,
|
|
1418
|
+
CI: "false",
|
|
1419
|
+
TERM: "xterm-256color",
|
|
1420
|
+
FORCE_COLOR: "1"
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
activeChildProcess = child;
|
|
1424
|
+
if (child.stdin) {
|
|
1425
|
+
child.stdin.on("error", () => {
|
|
1426
|
+
activeChildProcess = null;
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1388
1429
|
let stdout = "";
|
|
1389
1430
|
let stderr = "";
|
|
1390
1431
|
child.stdout.on("data", (data) => {
|
|
@@ -1398,6 +1439,7 @@ var init_exec_command = __esm({
|
|
|
1398
1439
|
if (onChunk) onChunk(chunk);
|
|
1399
1440
|
});
|
|
1400
1441
|
child.on("close", (code) => {
|
|
1442
|
+
activeChildProcess = null;
|
|
1401
1443
|
const result = [];
|
|
1402
1444
|
if (stdout) result.push(`STDOUT:
|
|
1403
1445
|
${stdout}`);
|
|
@@ -1416,6 +1458,7 @@ ${finalOutput}`);
|
|
|
1416
1458
|
}
|
|
1417
1459
|
});
|
|
1418
1460
|
child.on("error", (err) => {
|
|
1461
|
+
activeChildProcess = null;
|
|
1419
1462
|
resolve(`ERROR: Failed to start command [${command}]: ${err.message}`);
|
|
1420
1463
|
});
|
|
1421
1464
|
});
|
|
@@ -1712,7 +1755,6 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1712
1755
|
} else if (retryCount > 0) {
|
|
1713
1756
|
yield { type: "model_update", content: null };
|
|
1714
1757
|
}
|
|
1715
|
-
fs12.writeFileSync("contents.json", JSON.stringify(contents));
|
|
1716
1758
|
stream = await client.models.generateContentStream({
|
|
1717
1759
|
model: targetModel,
|
|
1718
1760
|
contents,
|
|
@@ -2346,11 +2388,12 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2346
2388
|
const [chatId, setChatId] = useState6(generateChatId());
|
|
2347
2389
|
const [activeCommand, setActiveCommand] = useState6(null);
|
|
2348
2390
|
const [execOutput, setExecOutput] = useState6("");
|
|
2391
|
+
const [isTerminalFocused, setIsTerminalFocused] = useState6(false);
|
|
2349
2392
|
const terminalEnv = useMemo(() => {
|
|
2350
2393
|
const isIDE = process.env.TERM_PROGRAM === "vscode" || !!process.env.VSC_TERMINAL_URL || !!process.env.INTELLIJ_TERMINAL_COMMAND_BLOCKS;
|
|
2351
2394
|
return {
|
|
2352
2395
|
isIDE,
|
|
2353
|
-
shortcut: isIDE ? "Shift+Enter" : "Ctrl+Enter"
|
|
2396
|
+
shortcut: isIDE ? "Shift + Enter" : "Ctrl + Enter"
|
|
2354
2397
|
};
|
|
2355
2398
|
}, []);
|
|
2356
2399
|
const activeCommandRef = useRef(null);
|
|
@@ -2421,6 +2464,24 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2421
2464
|
}, 0);
|
|
2422
2465
|
const maxLines = Math.max(1, wrappedLinesCount);
|
|
2423
2466
|
useInput4((inputText, key) => {
|
|
2467
|
+
if (key.tab && activeCommand) {
|
|
2468
|
+
setIsTerminalFocused((prev) => !prev);
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2471
|
+
if (isTerminalFocused && activeCommand) {
|
|
2472
|
+
if (key.return) {
|
|
2473
|
+
const isWin = process.platform === "win32";
|
|
2474
|
+
writeToActiveCommand(isWin ? "\r\n" : "\n");
|
|
2475
|
+
setExecOutput((prev) => prev + "\n");
|
|
2476
|
+
} else if (key.backspace || key.delete) {
|
|
2477
|
+
writeToActiveCommand("\b \b");
|
|
2478
|
+
setExecOutput((prev) => prev.slice(0, -1));
|
|
2479
|
+
} else if (inputText) {
|
|
2480
|
+
writeToActiveCommand(inputText);
|
|
2481
|
+
setExecOutput((prev) => prev + inputText);
|
|
2482
|
+
}
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2424
2485
|
if (maxLines > 2 && !isExpanded && activeView === "chat") {
|
|
2425
2486
|
if (key.backspace || key.delete) {
|
|
2426
2487
|
setInput("");
|
|
@@ -2432,13 +2493,14 @@ Check what's new using \`/changelog\` command.`,
|
|
|
2432
2493
|
}
|
|
2433
2494
|
}
|
|
2434
2495
|
if (key.escape) {
|
|
2435
|
-
if (isProcessing) {
|
|
2496
|
+
if (isProcessing || activeCommand) {
|
|
2436
2497
|
if (!escPressed) {
|
|
2437
2498
|
setEscPressed(true);
|
|
2438
2499
|
if (escTimer) clearTimeout(escTimer);
|
|
2439
2500
|
setEscTimer(setTimeout(() => setEscPressed(false), 3e3));
|
|
2440
2501
|
} else {
|
|
2441
2502
|
signalTermination();
|
|
2503
|
+
terminateActiveCommand();
|
|
2442
2504
|
setEscPressed(false);
|
|
2443
2505
|
if (escTimer) clearTimeout(escTimer);
|
|
2444
2506
|
}
|
|
@@ -2874,6 +2936,7 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2874
2936
|
return [...prev, { id: "term-" + Date.now(), role: "system", text: finalStatus, isTerminalRecord: true }];
|
|
2875
2937
|
});
|
|
2876
2938
|
setActiveCommand(null);
|
|
2939
|
+
setIsTerminalFocused(false);
|
|
2877
2940
|
setExecOutput("");
|
|
2878
2941
|
},
|
|
2879
2942
|
onToolApproval: async (tool, args) => {
|
|
@@ -3623,15 +3686,15 @@ Selection: ${val}`,
|
|
|
3623
3686
|
newline: (key) => key.return && key.shift || key.return && key.ctrl || key.return && key.leftAlt || key.return && key.rightAlt
|
|
3624
3687
|
}
|
|
3625
3688
|
}
|
|
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 === "" &&
|
|
3689
|
+
)))) : /* @__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 === "" && /* @__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" }, escPressed ? " Press ESC again to cancel the request." : !isProcessing ? ` Type /cmd or message... (${terminalEnv.shortcut} for newline)` : " You can send a prompt to steer the agent.")), /* @__PURE__ */ React10.createElement(
|
|
3627
3690
|
MultilineInput,
|
|
3628
3691
|
{
|
|
3692
|
+
focus: !isTerminalFocused,
|
|
3629
3693
|
value: input,
|
|
3630
3694
|
onChange: (val) => {
|
|
3631
3695
|
const cleanVal = val.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\s*\n/g, "\n");
|
|
3632
3696
|
setInput(cleanVal);
|
|
3633
3697
|
},
|
|
3634
|
-
placeholder: escPressed ? " Press ESC again to cancel the request." : ` Type /cmd or message... (${terminalEnv.shortcut} for newline)`,
|
|
3635
3698
|
onSubmit: handleSubmit,
|
|
3636
3699
|
maxRows: 3,
|
|
3637
3700
|
keyBindings: {
|
|
@@ -3649,7 +3712,7 @@ Selection: ${val}`,
|
|
|
3649
3712
|
showFullThinking,
|
|
3650
3713
|
columns: stdout?.columns || 80
|
|
3651
3714
|
}
|
|
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(
|
|
3715
|
+
), 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
3716
|
TextInput3,
|
|
3654
3717
|
{
|
|
3655
3718
|
value: tempKey,
|
|
@@ -3715,9 +3778,10 @@ var init_app = __esm({
|
|
|
3715
3778
|
init_arg_parser();
|
|
3716
3779
|
init_paths();
|
|
3717
3780
|
init_terminal();
|
|
3781
|
+
init_exec_command();
|
|
3718
3782
|
SESSION_START_TIME = Date.now();
|
|
3719
3783
|
CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
|
|
3720
|
-
versionFluxflow = "1.
|
|
3784
|
+
versionFluxflow = "1.4.1";
|
|
3721
3785
|
updatedOn = "2026-04-29";
|
|
3722
3786
|
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
3787
|
CommandMenu,
|