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 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
  });
@@ -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.3.6";
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.3.6",
3
+ "version": "1.4.0",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",