fluxflow-cli 1.3.5 → 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 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, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "cyan", bold: true }, completed ? "\u{1F3C1} FINISHED:" : "\u26A1 EXECUTING:", " "), /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "white" }, command)), /* @__PURE__ */ React.createElement(Text, { color: completed ? "#475569" : "yellow", bold: true }, completed ? "\u25CF ARCHIVED" : "\u25CF LIVE")), 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...")), !completed && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true, italic: true }, "Double-press ESC to terminate if hanging.")));
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").filter((l) => l.trim() !== "");
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
- return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginTop: i === 0 ? 0 : 1, marginBottom: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, trimmed.replace(/\*|#/g, "").trim()));
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
- return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginLeft: isBullet ? 2 : 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { italic: true, color: "gray", wrap: "anywhere" }, isBullet ? "\u2022 " : "", trimmed.replace(/^\*|\s\*/g, "").trim()));
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-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\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 30**. EACH HEADING SHOULD HAVE MIN 6, MAX 10 STEPS OF INTERNAL MONOLOGUE. EXPLORE EDGE CASES & NUANCES.\n\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\n-- END THINKING INSTRUCTIONS --",
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-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\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 8**, **MAX 25**. EACH HEADING SHOULD HAVE MIN 4, MAX 8 STEPS OF INTERNAL MONOLOGUE. EXPLORE EDGE CASES & THINK LONGER before responding.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\n-- END THINKING INSTRUCTIONS --",
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-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\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 9**. EACH HEADING SHOULD HAVE MIN 4, MAX 6 STEPS OF INTERNAL MONOLOGUE. THINK LONGER FOR COMPLEX QUERIES.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\n-- END THINKING INSTRUCTIONS --",
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-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\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 3**. EACH HEADING SHOULD HAVE MIN 1, MAX 2 STEPS 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.\n-- END THINKING INSTRUCTIONS --"
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. CRITICAL POLICY: KEEP SENTENCES IN TABLE **SHORT & CONCISE**. AND MAX 3 COLUMNS.
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, { shell: true, cwd: process.cwd() });
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
  });
@@ -1667,7 +1701,7 @@ USER_PROMPT: ${agentText}`.trim();
1667
1701
  TERMINATION_SIGNAL = false;
1668
1702
  let fullAgentResponseChunks = [];
1669
1703
  modifiedHistory.forEach((msg) => {
1670
- if (msg.text) {
1704
+ if (msg.text && msg.role === "agent") {
1671
1705
  msg.text = msg.text.replace(/<(think|thought)>[\s\S]*?<\/(think|thought)>/gi, "").trim();
1672
1706
  }
1673
1707
  });
@@ -1716,10 +1750,9 @@ USER_PROMPT: ${agentText}`.trim();
1716
1750
  model: targetModel,
1717
1751
  contents,
1718
1752
  config: {
1719
- temperature: mode === "Flux" ? 0.95 : 1.2,
1720
1753
  thinkingConfig: {
1721
1754
  includeThoughts: false,
1722
- thinkingLevel: targetModel === "gemini-3.1-pro-preview" ? ThinkingLevel.MEDIUM : ThinkingLevel.MINIMAL
1755
+ thinkingLevel: ThinkingLevel.MINIMAL
1723
1756
  }
1724
1757
  }
1725
1758
  });
@@ -1952,7 +1985,6 @@ ${boxBottom}
1952
1985
  model: janitorModel || "gemma-4-26b-a4b-it",
1953
1986
  contents: janitorContents,
1954
1987
  config: {
1955
- temperature: 0.5,
1956
1988
  thinkingConfig: {
1957
1989
  includeThoughts: false,
1958
1990
  thinkingLevel: ThinkingLevel.MINIMAL
@@ -2347,6 +2379,7 @@ Check what's new using \`/changelog\` command.`,
2347
2379
  const [chatId, setChatId] = useState6(generateChatId());
2348
2380
  const [activeCommand, setActiveCommand] = useState6(null);
2349
2381
  const [execOutput, setExecOutput] = useState6("");
2382
+ const [isTerminalFocused, setIsTerminalFocused] = useState6(false);
2350
2383
  const terminalEnv = useMemo(() => {
2351
2384
  const isIDE = process.env.TERM_PROGRAM === "vscode" || !!process.env.VSC_TERMINAL_URL || !!process.env.INTELLIJ_TERMINAL_COMMAND_BLOCKS;
2352
2385
  return {
@@ -2422,6 +2455,24 @@ Check what's new using \`/changelog\` command.`,
2422
2455
  }, 0);
2423
2456
  const maxLines = Math.max(1, wrappedLinesCount);
2424
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
+ }
2425
2476
  if (maxLines > 2 && !isExpanded && activeView === "chat") {
2426
2477
  if (key.backspace || key.delete) {
2427
2478
  setInput("");
@@ -2875,6 +2926,7 @@ OUTPUT: ${execOutputRef.current}`;
2875
2926
  return [...prev, { id: "term-" + Date.now(), role: "system", text: finalStatus, isTerminalRecord: true }];
2876
2927
  });
2877
2928
  setActiveCommand(null);
2929
+ setIsTerminalFocused(false);
2878
2930
  setExecOutput("");
2879
2931
  },
2880
2932
  onToolApproval: async (tool, args) => {
@@ -3624,15 +3676,15 @@ Selection: ${val}`,
3624
3676
  newline: (key) => key.return && key.shift || key.return && key.ctrl || key.return && key.leftAlt || key.return && key.rightAlt
3625
3677
  }
3626
3678
  }
3627
- )))) : /* @__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(
3628
3680
  MultilineInput,
3629
3681
  {
3682
+ focus: !isTerminalFocused,
3630
3683
  value: input,
3631
3684
  onChange: (val) => {
3632
3685
  const cleanVal = val.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\s*\n/g, "\n");
3633
3686
  setInput(cleanVal);
3634
3687
  },
3635
- placeholder: escPressed ? " Press ESC again to cancel the request." : ` Type /cmd or message... (${terminalEnv.shortcut} for newline)`,
3636
3688
  onSubmit: handleSubmit,
3637
3689
  maxRows: 3,
3638
3690
  keyBindings: {
@@ -3650,7 +3702,7 @@ Selection: ${val}`,
3650
3702
  showFullThinking,
3651
3703
  columns: stdout?.columns || 80
3652
3704
  }
3653
- ), 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(
3654
3706
  TextInput3,
3655
3707
  {
3656
3708
  value: tempKey,
@@ -3716,9 +3768,10 @@ var init_app = __esm({
3716
3768
  init_arg_parser();
3717
3769
  init_paths();
3718
3770
  init_terminal();
3771
+ init_exec_command();
3719
3772
  SESSION_START_TIME = Date.now();
3720
3773
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
3721
- versionFluxflow = "1.3.5";
3774
+ versionFluxflow = "1.4.0";
3722
3775
  updatedOn = "2026-04-29";
3723
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(
3724
3777
  CommandMenu,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.3.5",
3
+ "version": "1.4.0",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",