fluxflow-cli 1.5.4 → 1.6.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/TOOLS.md CHANGED
@@ -1,52 +1,52 @@
1
- # 🧰 Agent Tools & Capabilities
2
-
3
- Flux Flow provides a robust set of tools that allow the AI to interact with the file system, execute code, and search the web. The availability of these tools depends on the active operating mode.
4
-
5
- ## Tool Availability by Mode
6
-
7
- | Tool | Flux Mode (Dev) | Flow Mode (Chat) |
8
- | :--- | :---: | :---: |
9
- | **Web Search** | ✅ | ✅ |
10
- | **Web Scrape** | ✅ | ✅ |
11
- | **Write PDF** | ✅ | ✅ |
12
- | **View/Read Files** | ✅ | ❌ |
13
- | **Write/Update Files** | ✅ | ❌ |
14
- | **Execute Commands** | ✅ | ❌ |
15
-
16
- ---
17
-
18
- ## Core Tools
19
-
20
- ### 🌐 Web & Research
21
- - **`web_search`**: Uses DuckDuckGo to find up-to-date information on the internet. Crucial for answering questions about recent events or unlearned documentation.
22
- - **`web_scrape`**: Extracts the detailed text content from a specific URL, allowing the agent to read documentation or articles.
23
- - **`write_pdf`**: Generates high-fidelity, branded PDF documents from HTML/CSS. Features automatic watermarking and page-aware layout management.
24
-
25
- ### 📁 File System Operations
26
- - **`list_files`**: Lists the contents of a directory to help the agent understand the project structure.
27
- - **`read_folder`**: Provides detailed statistics and metadata about a directory's contents.
28
- - **`view_file`**: Reads the content of a file.
29
- - **Native Multimodality**: Supports analyzing images (JPG, PNG, WEBP) and PDF documents. The tool automatically detects binary formats and encodes them for AI analysis.
30
- - **Text Reading**: Supports specific line ranges (`start_line`, `end_line`) to manage context size efficiently.
31
-
32
- ### ✍️ Code Editing
33
- - **`write_file`**: Creates a new file or completely overwrites an existing one with new content.
34
- - **`update_file` (Smart Patching)**: Surgically replaces a specific block of text within a file.
35
- - *Diff Generation*: It returns a high-fidelity visual diff (Red/Green changes with context lines) to the UI, allowing the user to see exactly what the agent modified.
36
-
37
- ### 💻 Terminal Execution
38
- - **`exec_command`**: Runs a shell command directly in the terminal using Node's `child_process.spawn`.
39
- - *Context Aware*: Runs in the current working directory.
40
- - *Cross-Platform*: Uses `shell: true` to handle Windows `.cmd`/`.bat` files natively.
41
-
42
- ---
43
-
44
- ## Memory Management
45
-
46
- The memory tool (`memory.js`) is primarily used by the background **Janitor** model, but can be accessed by the main agent if necessary.
47
-
48
- - **Temporary Context (`action='temp'`)**: Saves a rolling summary of the current session to maintain conversational context without bloating the main prompt history.
49
- - **Persistent User Memory (`action='user'`)**:
50
- - The Janitor analyzes conversations to detect user preferences, hobbies, or instructions.
51
- - It uses `add`, `update`, or `delete` methods to manage facts in the encrypted `memories.json` vault.
1
+ # 🧰 Agent Tools & Capabilities
2
+
3
+ Flux Flow provides a robust set of tools that allow the AI to interact with the file system, execute code, and search the web. The availability of these tools depends on the active operating mode.
4
+
5
+ ## Tool Availability by Mode
6
+
7
+ | Tool | Flux Mode (Dev) | Flow Mode (Chat) |
8
+ | :--- | :---: | :---: |
9
+ | **Web Search** | ✅ | ✅ |
10
+ | **Web Scrape** | ✅ | ✅ |
11
+ | **Write PDF** | ✅ | ✅ |
12
+ | **View/Read Files** | ✅ | ❌ |
13
+ | **Write/Update Files** | ✅ | ❌ |
14
+ | **Execute Commands** | ✅ | ❌ |
15
+
16
+ ---
17
+
18
+ ## Core Tools
19
+
20
+ ### 🌐 Web & Research
21
+ - **`web_search`**: Uses DuckDuckGo to find up-to-date information on the internet. Crucial for answering questions about recent events or unlearned documentation.
22
+ - **`web_scrape`**: Extracts the detailed text content from a specific URL, allowing the agent to read documentation or articles.
23
+ - **`write_pdf`**: Generates high-fidelity, branded PDF documents from HTML/CSS. Features automatic watermarking and page-aware layout management.
24
+
25
+ ### 📁 File System Operations
26
+ - **`list_files`**: Lists the contents of a directory to help the agent understand the project structure.
27
+ - **`read_folder`**: Provides detailed statistics and metadata about a directory's contents.
28
+ - **`view_file`**: Reads the content of a file.
29
+ - **Native Multimodality**: Supports analyzing images (JPG, PNG, WEBP) and PDF documents. The tool automatically detects binary formats and encodes them for AI analysis.
30
+ - **Text Reading**: Supports specific line ranges (`start_line`, `end_line`) to manage context size efficiently.
31
+
32
+ ### ✍️ Code Editing
33
+ - **`write_file`**: Creates a new file or completely overwrites an existing one with new content.
34
+ - **`update_file` (Smart Patching)**: Surgically replaces a specific block of text within a file.
35
+ - *Diff Generation*: It returns a high-fidelity visual diff (Red/Green changes with context lines) to the UI, allowing the user to see exactly what the agent modified.
36
+
37
+ ### 💻 Terminal Execution
38
+ - **`exec_command`**: Runs a shell command directly in the terminal using Node's `child_process.spawn`.
39
+ - *Context Aware*: Runs in the current working directory.
40
+ - *Cross-Platform*: Uses `shell: true` to handle Windows `.cmd`/`.bat` files natively.
41
+
42
+ ---
43
+
44
+ ## Memory Management
45
+
46
+ The memory tool (`memory.js`) is primarily used by the background **Janitor** model, but can be accessed by the main agent if necessary.
47
+
48
+ - **Temporary Context (`action='temp'`)**: Saves a rolling summary of the current session to maintain conversational context without bloating the main prompt history.
49
+ - **Persistent User Memory (`action='user'`)**:
50
+ - The Janitor analyzes conversations to detect user preferences, hobbies, or instructions.
51
+ - It uses `add`, `update`, or `delete` methods to manage facts in the encrypted `memories.json` vault.
52
52
  - These memories are injected into the system prompt of *future* sessions, allowing Flux Flow to learn and adapt to the user over time.
package/UI_FEATURES.md CHANGED
@@ -1,101 +1,101 @@
1
- # 🎮 User Interface & Interaction Features
2
-
3
- Flux Flow is designed to be a high-performance terminal application. Beyond basic chat, it includes a variety of advanced UI features and human-in-the-loop controls to keep you in command of the agent.
4
-
5
- ## ⌨️ Command System
6
-
7
- You can control the application using `/` commands directly in the chat input:
8
-
9
- - **/mode [flux|flow]**: Quickly switch between **Flux** (Dev) and **Flow** (Chat) modes. Using it without arguments opens the selection menu.
10
- - **/thinking [low|medium|high|max|show|hide]**: Adjust reasoning depth or toggle visibility of the thinking process. Using it without arguments opens the selection menu.
11
- - **/model [name]**: Choose which AI model to use for the main interaction.
12
- - **/key**: Open the API Key management view to update or remove your credentials.
13
- - **/settings**: Access the system configuration menu.
14
- - **/profile**: Update your name, nickname, and custom instructions.
15
- - **/update**: Update Flux Flow to the latest version.
16
- - **/memory**: View and manage the persistent memories extracted by the Janitor.
17
- - **/resume <chat-id>**: Switch back to a previous conversation.
18
- - **/changelog**: Open the project's changelog in your default browser.
19
- - **/help**: List all available commands.
20
-
21
- ## 📁 External Data Sanctuary (Redirection)
22
-
23
- Flux Flow allows you to "Anchor" your data outside of the default user directory. This is perfect for users who want to keep their logs, chat history, and encrypted memories on an external drive, a VeraCrypt volume, or a specific project folder.
24
-
25
- - **How to Enable**: Open `/settings` and toggle **Use External Data**.
26
- - **Portability**: Once set, the application will synchronously "pivot" all data operations to your specified `externalDataPath` upon startup.
27
- - **Privacy**: Keeps sensitive data off your primary system drive.
28
-
29
-
30
- ### Command Shortcuts
31
-
32
- For power users, several commands support direct arguments to skip the menus:
33
- - ` /mode flux ` or ` /mode flow `
34
- - ` /thinking low ` / ` /thinking medium ` / ` /thinking high ` / ` /thinking max `
35
- - ` /thinking show ` / ` /thinking hide ` (Toggles thinking process visibility)
36
- - ` /model gemini-3.1-pro-preview ` (Switches model directly)
37
- - ` /update check ` (Checks for updates)
38
-
39
- ## 🧠 Thinking Levels & Visualization
40
-
41
- Flux Flow separates the model's "internal monologue" (reasoning) from its final response using `<think>` tags.
42
-
43
- - **Thinking Levels**: Depending on the mode, you can choose from **Low**, **Medium**, **High**, or **Max**. Higher levels allow the agent more "space" to reason through complex architecture or debugging problems.
44
- - **Show/Hide Thinking**: You can toggle the visibility of the thinking process using `/thinking show/hide`.
45
- - 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.
46
-
47
- ## ⚡ Interactive Sub-Terminal
48
-
49
- Flux Flow features a high-fidelity, interactive sub-terminal that allows you to engage with commands spawned by the agent in real-time.
50
-
51
- - **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.
52
- - **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.
53
- - **Visual Feedback**:
54
- - **Yellow Glow**: When the terminal is focused, its border changes to a "Double Yellow" style to indicate it is capturing input.
55
- - **Status Indicator**: The footer will change from `● LIVE` to `▶ TERMINAL FOCUSED`.
56
- - **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.
57
- - **Input Mirroring**: Your keystrokes are mirrored in the terminal box in real-time, providing an IDE-grade responsive feel.
58
-
59
- > [!TIP]
60
- > 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.
61
-
62
- ## 🛡️ Human-in-the-Loop (HITL) Verification
63
-
64
- Security and safety are paramount when an AI has access to your file system and terminal. Flux Flow implements several layers of verification:
65
-
66
- ### Tool Approval
67
- By default, the agent **cannot** execute dangerous actions without your consent.
68
- - **Terminal Approval**: If the agent attempts to run a shell command (`exec_command`), a dedicated approval screen appears showing the exact command. You can **Allow** or **Deny** the execution.
69
- - **File Approval**: Similar to terminal commands, writing or updating files requires manual approval unless configured otherwise.
70
- - **Safe Commands**: Basic read-only commands (like `ls`, `git status`, `pwd`) are automatically allowed to minimize friction.
71
-
72
- ### Auto-Execution (Advanced)
73
- For power users, **Auto-Exec** can be enabled in `/settings`.
74
- - **⚠️ Warning**: This allows the agent to run any tool and execute any command autonomously.
75
- - **External Access**: You can also toggle whether the agent is allowed to access files outside of its current working directory.
76
-
77
- ## 🔄 Steering & Resolution
78
-
79
- ### Real-time Steering
80
- If you realize the agent is going down the wrong path *while* it is in an agentic loop, you can provide "Steering Hints." The system will inject your feedback into the next loop to course-correct the agent.
81
-
82
- ### Resolution Modal
83
- If the agent finishes its task just as you send a steering hint, a **Resolution Modal** appears. It asks if you want to:
84
- - **Send Anyway**: Start a new loop with your feedback.
85
- - **Edit Prompt**: Refine your feedback before sending.
86
-
87
- ## 📊 Status Bar & Feedback
88
- The bottom of the screen features a dynamic status bar showing:
89
- - **Active Mode** (Flux/Flow)
90
- - **Thinking Level**
91
- - **Token Usage**: Real-time tracking of tokens used in the current session.
92
- - **Agentic Loops**: Counters showing how many times the agent has "looped" to solve the current task.
93
- - **API Status**: Visual feedback when the model is thinking or executing a tool.
94
-
95
- ## 🚑 System Integrity & Self-Healing
96
-
97
- Flux Flow is a "Self-Healing" agent. It actively monitors its own environment to ensure all complex dependencies (like the Chromium engine for PDF generation) are ready for action.
98
-
99
- - **Startup Integrity Check**: On launch, Flux performs a "Heartbeat Check" on its internal engines.
100
- - **Automatic Recovery**: If a dependency is missing, you will see a `🔧 [SYSTEM] Initializing...` message. Flux will autonomously download and configure the required binaries using `pnpm` or `npx` fallbacks, keeping you informed every step of the way.
1
+ # 🎮 User Interface & Interaction Features
2
+
3
+ Flux Flow is designed to be a high-performance terminal application. Beyond basic chat, it includes a variety of advanced UI features and human-in-the-loop controls to keep you in command of the agent.
4
+
5
+ ## ⌨️ Command System
6
+
7
+ You can control the application using `/` commands directly in the chat input:
8
+
9
+ - **/mode [flux|flow]**: Quickly switch between **Flux** (Dev) and **Flow** (Chat) modes. Using it without arguments opens the selection menu.
10
+ - **/thinking [low|medium|high|max|show|hide]**: Adjust reasoning depth or toggle visibility of the thinking process. Using it without arguments opens the selection menu.
11
+ - **/model [name]**: Choose which AI model to use for the main interaction.
12
+ - **/key**: Open the API Key management view to update or remove your credentials.
13
+ - **/settings**: Access the system configuration menu.
14
+ - **/profile**: Update your name, nickname, and custom instructions.
15
+ - **/update**: Update Flux Flow to the latest version.
16
+ - **/memory**: View and manage the persistent memories extracted by the Janitor.
17
+ - **/resume <chat-id>**: Switch back to a previous conversation.
18
+ - **/changelog**: Open the project's changelog in your default browser.
19
+ - **/help**: List all available commands.
20
+
21
+ ## 📁 External Data Sanctuary (Redirection)
22
+
23
+ Flux Flow allows you to "Anchor" your data outside of the default user directory. This is perfect for users who want to keep their logs, chat history, and encrypted memories on an external drive, a VeraCrypt volume, or a specific project folder.
24
+
25
+ - **How to Enable**: Open `/settings` and toggle **Use External Data**.
26
+ - **Portability**: Once set, the application will synchronously "pivot" all data operations to your specified `externalDataPath` upon startup.
27
+ - **Privacy**: Keeps sensitive data off your primary system drive.
28
+
29
+
30
+ ### Command Shortcuts
31
+
32
+ For power users, several commands support direct arguments to skip the menus:
33
+ - ` /mode flux ` or ` /mode flow `
34
+ - ` /thinking low ` / ` /thinking medium ` / ` /thinking high ` / ` /thinking max `
35
+ - ` /thinking show ` / ` /thinking hide ` (Toggles thinking process visibility)
36
+ - ` /model gemini-3.1-pro-preview ` (Switches model directly)
37
+ - ` /update check ` (Checks for updates)
38
+
39
+ ## 🧠 Thinking Levels & Visualization
40
+
41
+ Flux Flow separates the model's "internal monologue" (reasoning) from its final response using `<think>` tags.
42
+
43
+ - **Thinking Levels**: Depending on the mode, you can choose from **Low**, **Medium**, **High**, or **Max**. Higher levels allow the agent more "space" to reason through complex architecture or debugging problems.
44
+ - **Show/Hide Thinking**: You can toggle the visibility of the thinking process using `/thinking show/hide`.
45
+ - 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.
46
+
47
+ ## ⚡ Interactive Sub-Terminal
48
+
49
+ Flux Flow features a high-fidelity, interactive sub-terminal that allows you to engage with commands spawned by the agent in real-time.
50
+
51
+ - **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.
52
+ - **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.
53
+ - **Visual Feedback**:
54
+ - **Yellow Glow**: When the terminal is focused, its border changes to a "Double Yellow" style to indicate it is capturing input.
55
+ - **Status Indicator**: The footer will change from `● LIVE` to `▶ TERMINAL FOCUSED`.
56
+ - **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.
57
+ - **Input Mirroring**: Your keystrokes are mirrored in the terminal box in real-time, providing an IDE-grade responsive feel.
58
+
59
+ > [!TIP]
60
+ > 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.
61
+
62
+ ## 🛡️ Human-in-the-Loop (HITL) Verification
63
+
64
+ Security and safety are paramount when an AI has access to your file system and terminal. Flux Flow implements several layers of verification:
65
+
66
+ ### Tool Approval
67
+ By default, the agent **cannot** execute dangerous actions without your consent.
68
+ - **Terminal Approval**: If the agent attempts to run a shell command (`exec_command`), a dedicated approval screen appears showing the exact command. You can **Allow** or **Deny** the execution.
69
+ - **File Approval**: Similar to terminal commands, writing or updating files requires manual approval unless configured otherwise.
70
+ - **Safe Commands**: Basic read-only commands (like `ls`, `git status`, `pwd`) are automatically allowed to minimize friction.
71
+
72
+ ### Auto-Execution (Advanced)
73
+ For power users, **Auto-Exec** can be enabled in `/settings`.
74
+ - **⚠️ Warning**: This allows the agent to run any tool and execute any command autonomously.
75
+ - **External Access**: You can also toggle whether the agent is allowed to access files outside of its current working directory.
76
+
77
+ ## 🔄 Steering & Resolution
78
+
79
+ ### Real-time Steering
80
+ If you realize the agent is going down the wrong path *while* it is in an agentic loop, you can provide "Steering Hints." The system will inject your feedback into the next loop to course-correct the agent.
81
+
82
+ ### Resolution Modal
83
+ If the agent finishes its task just as you send a steering hint, a **Resolution Modal** appears. It asks if you want to:
84
+ - **Send Anyway**: Start a new loop with your feedback.
85
+ - **Edit Prompt**: Refine your feedback before sending.
86
+
87
+ ## 📊 Status Bar & Feedback
88
+ The bottom of the screen features a dynamic status bar showing:
89
+ - **Active Mode** (Flux/Flow)
90
+ - **Thinking Level**
91
+ - **Token Usage**: Real-time tracking of tokens used in the current session.
92
+ - **Agentic Loops**: Counters showing how many times the agent has "looped" to solve the current task.
93
+ - **API Status**: Visual feedback when the model is thinking or executing a tool.
94
+
95
+ ## 🚑 System Integrity & Self-Healing
96
+
97
+ Flux Flow is a "Self-Healing" agent. It actively monitors its own environment to ensure all complex dependencies (like the Chromium engine for PDF generation) are ready for action.
98
+
99
+ - **Startup Integrity Check**: On launch, Flux performs a "Heartbeat Check" on its internal engines.
100
+ - **Automatic Recovery**: If a dependency is missing, you will see a `🔧 [SYSTEM] Initializing...` message. Flux will autonomously download and configure the required binaries using `pnpm` or `npx` fallbacks, keeping you informed every step of the way.
101
101
  - **Silent Maintenance**: Once the engine is ready, you'll receive a `✅ [SYSTEM] All dependencies installed successfully.` confirmation.
package/dist/fluxflow.js CHANGED
@@ -23,39 +23,81 @@ var init_TerminalBox = __esm({
23
23
  });
24
24
 
25
25
  // src/components/ChatLayout.jsx
26
- import React2 from "react";
26
+ import React2, { useState, useEffect, useRef } from "react";
27
27
  import { Box as Box2, Text as Text2 } from "ink";
28
- var cleanSignals, formatThinkText, InlineMarkdown, wrapText, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, MessageItem, ChatLayout, ChatLayout_default;
28
+ var TypewriterText, cleanSignals, formatThinkText, InlineMarkdown, wrapText, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, MessageItem, ChatLayout, ChatLayout_default;
29
29
  var init_ChatLayout = __esm({
30
30
  "src/components/ChatLayout.jsx"() {
31
31
  init_TerminalBox();
32
+ TypewriterText = ({ text, isStreaming, onComplete, columns = 80, color = "white", speed = 50, render }) => {
33
+ const [displayedText, setDisplayedText] = useState("");
34
+ const fullTextRef = useRef(text);
35
+ const displayedTextRef = useRef("");
36
+ useEffect(() => {
37
+ fullTextRef.current = text;
38
+ }, [text]);
39
+ useEffect(() => {
40
+ const timer = setInterval(() => {
41
+ const currentFull = fullTextRef.current;
42
+ const currentDisp = displayedTextRef.current;
43
+ if (currentDisp.length < currentFull.length) {
44
+ const remaining = currentFull.slice(currentDisp.length);
45
+ const match = remaining.match(/^(\S+\s*|\s+)/);
46
+ if (match) {
47
+ const chunk = match[0];
48
+ const gap = currentFull.length - currentDisp.length;
49
+ let revealedChunk = chunk;
50
+ if (gap > 100) {
51
+ const extraMatch = remaining.slice(chunk.length).match(/^(\S+\s*|\s+){0,2}/);
52
+ if (extraMatch) revealedChunk += extraMatch[0];
53
+ }
54
+ const nextText = currentDisp + revealedChunk;
55
+ setDisplayedText(nextText);
56
+ displayedTextRef.current = nextText;
57
+ }
58
+ } else if (!isStreaming) {
59
+ if (onComplete) onComplete();
60
+ clearInterval(timer);
61
+ }
62
+ }, speed);
63
+ return () => clearInterval(timer);
64
+ }, [isStreaming, speed]);
65
+ if (render) return render(displayedText);
66
+ return /* @__PURE__ */ React2.createElement(CodeRenderer, { text: displayedText, columns });
67
+ };
32
68
  cleanSignals = (text) => {
33
69
  if (!text) return text;
34
- const parts = [];
35
- let i = 0;
36
- while (i < text.length) {
37
- const trigger = "tool:functions.";
38
- if (text.substring(i, i + trigger.length).toLowerCase() === trigger) {
39
- let balance = 0;
40
- let foundStart = false;
41
- let j = i;
42
- while (j < text.length) {
43
- if (text[j] === "(") {
70
+ let result = text;
71
+ const trigger = "tool:functions.";
72
+ while (true) {
73
+ const lowerResult = result.toLowerCase();
74
+ const startIdx = lowerResult.indexOf(trigger);
75
+ if (startIdx === -1) break;
76
+ let balance = 0;
77
+ let foundStart = false;
78
+ let inString = null;
79
+ let j = startIdx;
80
+ while (j < result.length) {
81
+ const char = result[j];
82
+ if (!inString && (char === "'" || char === '"' || char === "`")) {
83
+ inString = char;
84
+ } else if (inString && char === inString && result[j - 1] !== "\\") {
85
+ inString = null;
86
+ }
87
+ if (!inString) {
88
+ if (char === "(") {
44
89
  balance++;
45
90
  foundStart = true;
46
- } else if (text[j] === ")") {
91
+ } else if (char === ")") {
47
92
  balance--;
48
93
  }
49
- j++;
50
- if (foundStart && balance === 0) break;
51
94
  }
52
- i = j;
53
- } else {
54
- parts.push(text[i]);
55
- i++;
95
+ j++;
96
+ if (foundStart && balance === 0 && !inString) break;
56
97
  }
98
+ result = result.substring(0, startIdx) + result.substring(j);
57
99
  }
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();
100
+ return result.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
101
  };
60
102
  formatThinkText = (cleaned, columns = 80) => {
61
103
  if (!cleaned) return null;
@@ -94,7 +136,7 @@ var init_ChatLayout = __esm({
94
136
  if (content.startsWith("\\text{") && content.endsWith("}")) {
95
137
  content = content.slice(6, -1);
96
138
  }
97
- const mathContent = content.replace(/\\multiply/g, "\xD7").replace(/\\divide/g, "\xF7");
139
+ const mathContent = content.replace(/\\multiply/g, "\xD7").replace(/\\mul/g, "\xD7").replace(/\\div/g, "\xF7");
98
140
  return /* @__PURE__ */ React2.createElement(Text2, { key: j, color: "white", backgroundColor: "#4c0099", bold: true, italic: true }, " ", mathContent, " ");
99
141
  }
100
142
  if (part.startsWith("[") && (part.includes("](") || part.includes("] ("))) {
@@ -293,7 +335,11 @@ var init_ChatLayout = __esm({
293
335
  const outputList = outputMatch ? outputMatch[1] : "";
294
336
  return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(TerminalBox, { command: cmd, output: outputList, completed: true }));
295
337
  }
338
+ const [animationDone, setAnimationDone] = React2.useState(!msg.isStreaming);
296
339
  const content = React2.useMemo(() => cleanSignals(msg.text), [msg.text]);
340
+ React2.useEffect(() => {
341
+ if (msg.isStreaming) setAnimationDone(false);
342
+ }, [msg.id]);
297
343
  const finalContent = React2.useMemo(() => {
298
344
  if (msg.role === "think" && !showFullThinking) {
299
345
  const lines = content.split("\n").filter((line) => {
@@ -320,7 +366,24 @@ var init_ChatLayout = __esm({
320
366
  finalContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\n/g, "\n").replace(/\\$/, ""),
321
367
  columns - 6
322
368
  ).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))))
323
- ) : 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]"))));
369
+ ) : 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%" }, !animationDone ? /* @__PURE__ */ React2.createElement(
370
+ TypewriterText,
371
+ {
372
+ text: finalContent,
373
+ isStreaming: msg.isStreaming,
374
+ onComplete: () => setAnimationDone(true),
375
+ speed: 25,
376
+ render: (t) => formatThinkText(t, columns)
377
+ }
378
+ ) : formatThinkText(finalContent, columns))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1, width: "100%" }, !animationDone ? /* @__PURE__ */ React2.createElement(
379
+ TypewriterText,
380
+ {
381
+ text: finalContent,
382
+ isStreaming: msg.isStreaming,
383
+ onComplete: () => setAnimationDone(true),
384
+ columns
385
+ }
386
+ ) : /* @__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]"))));
324
387
  });
325
388
  ChatLayout = React2.memo(({ messages, showFullThinking, columns = 80 }) => {
326
389
  return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, messages.map((msg, idx) => /* @__PURE__ */ React2.createElement(
@@ -392,13 +455,13 @@ var init_CommandMenu = __esm({
392
455
  });
393
456
 
394
457
  // src/components/ProfileForm.jsx
395
- import React5, { useState } from "react";
458
+ import React5, { useState as useState2 } from "react";
396
459
  import { Box as Box5, Text as Text5 } from "ink";
397
460
  import TextInput from "ink-text-input";
398
461
  function ProfileForm({ onSave, onCancel }) {
399
- const [step, setStep] = useState(0);
400
- const [currentInput, setCurrentInput] = useState("");
401
- const [profile, setProfile] = useState({ name: "", nickname: "", instructions: "" });
462
+ const [step, setStep] = useState2(0);
463
+ const [currentInput, setCurrentInput] = useState2("");
464
+ const [profile, setProfile] = useState2({ name: "", nickname: "", instructions: "" });
402
465
  const steps = [
403
466
  { key: "name", label: "Enter your Name: " },
404
467
  { key: "nickname", label: "Enter a Nickname (Agent will use this): " },
@@ -430,16 +493,16 @@ var init_ProfileForm = __esm({
430
493
  });
431
494
 
432
495
  // src/components/AskUserModal.jsx
433
- import React6, { useState as useState2 } from "react";
496
+ import React6, { useState as useState3 } from "react";
434
497
  import { Box as Box6, Text as Text6, useInput } from "ink";
435
498
  import TextInput2 from "ink-text-input";
436
499
  var AskUserModal, AskUserModal_default;
437
500
  var init_AskUserModal = __esm({
438
501
  "src/components/AskUserModal.jsx"() {
439
502
  AskUserModal = ({ question, options, onResolve }) => {
440
- const [isSuggestingElse, setIsSuggestingElse] = useState2(false);
441
- const [customInput, setCustomInput] = useState2("");
442
- const [selectedIndex, setSelectedIndex] = useState2(0);
503
+ const [isSuggestingElse, setIsSuggestingElse] = useState3(false);
504
+ const [customInput, setCustomInput] = useState3("");
505
+ const [selectedIndex, setSelectedIndex] = useState3(0);
443
506
  const allOptions = [...options, { id: "CUSTOM", label: "Suggest something else...", description: "Provide a custom response" }];
444
507
  useInput((input, key) => {
445
508
  if (isSuggestingElse) return;
@@ -618,7 +681,7 @@ ${mode === "Flux" ? `
618
681
  6. Write PDF: tool:functions.write_pdf(path="path", content="<html/css content>", orientation="portrait/landscape"). Generates a professional PDF document. Orientation are optional. A4 size page will be used, so any multi-page PDFs calculate your alightment and page breaks to not mess up A4 page layout . DO NOT ADD FOOTER MANUALLY, the system will handle it automatically. USE CSS TO VISUALLY BEAUTIFY THE DOCUMENT.
619
682
  7. Execution: tool:functions.exec_command(command="terminal command"). Runs a shell command.
620
683
 
621
- **NOTE:** WHEN WRITING/UPDATING FILES, USE ACTUAL NEW LINE CHARACTER FOR LINE BREAKS RATHER THAN STRING '\\n'`.trim() : `
684
+ **NOTE:** WHEN WRITING/UPDATING FILES, USE ACTUAL NEW LINE CONTROL CHARACTER (LF) FOR LINE BREAKS RATHER THAN STRING '\\n'`.trim() : `
622
685
  - DEV & FILE TOOLS ARE NOT AVAILABLE IN FLOW MODE. If you need to access files, tell the user to switch to FLUX MODE (manually by user).`.trim()}
623
686
  -----------------
624
687
  Results will be provided in the next loop as: [TOOL_RESULT]: [content]
@@ -960,16 +1023,16 @@ var init_arg_parser = __esm({
960
1023
  "src/utils/arg_parser.js"() {
961
1024
  parseArgs = (argsString) => {
962
1025
  const args = {};
963
- const regex = /(\w+)\s*=\s*(?:(["'])((?:\\.|(?!\2)[\s\S])*)\2|([^,\s\)]+))/g;
1026
+ const regex = /(\w+)\s*=\s*(?:(["'`])((?:\\.|(?!\2)[\s\S])*)\2|([^,\s\)]+))/g;
964
1027
  let match;
965
1028
  while ((match = regex.exec(argsString)) !== null) {
966
1029
  const key = match[1];
967
1030
  let value = match[3] !== void 0 ? match[3] : match[4];
968
1031
  if (match[3] !== void 0) {
969
1032
  try {
970
- value = JSON.parse(`"${value.replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`);
1033
+ value = JSON.parse(`"${value.replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r")}"`);
971
1034
  } catch (e) {
972
- value = value.replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, "\\");
1035
+ value = value.replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\`/g, "`").replace(/\\\\/g, "\\");
973
1036
  }
974
1037
  }
975
1038
  if (value === "true") value = true;
@@ -1366,7 +1429,11 @@ var init_view_file = __esm({
1366
1429
  }
1367
1430
  };
1368
1431
  }
1369
- const content = fs9.readFileSync(absolutePath, "utf8");
1432
+ let content = fs9.readFileSync(absolutePath, "utf8");
1433
+ if (content.startsWith("\uFEFF")) {
1434
+ content = content.slice(1);
1435
+ }
1436
+ content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1370
1437
  const lines = content.split("\n");
1371
1438
  const totalLines = lines.length;
1372
1439
  const start = Math.max(0, start_line - 1);
@@ -1395,7 +1462,7 @@ var init_write_file = __esm({
1395
1462
  let { path: targetPath, content } = parseArgs(args);
1396
1463
  if (!targetPath) return 'ERROR: Missing "path" argument for write_file.';
1397
1464
  if (content === void 0) return 'ERROR: Missing "content" argument for write_file.';
1398
- content = content.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
1465
+ content = content.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1399
1466
  const absolutePath = path10.resolve(process.cwd(), targetPath);
1400
1467
  const parentDir = path10.dirname(absolutePath);
1401
1468
  try {
@@ -1465,7 +1532,7 @@ var init_update_file = __esm({
1465
1532
  if (!targetPath) return 'ERROR: Missing "path" argument for update_file.';
1466
1533
  if (content_to_replace === void 0) return 'ERROR: Missing "content_to_replace" argument.';
1467
1534
  if (content_to_add === void 0) return 'ERROR: Missing "content_to_add" argument.';
1468
- const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
1535
+ const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1469
1536
  content_to_replace = strip(content_to_replace);
1470
1537
  content_to_add = strip(content_to_add);
1471
1538
  const absolutePath = path11.resolve(process.cwd(), targetPath);
@@ -1473,9 +1540,23 @@ var init_update_file = __esm({
1473
1540
  if (!fs11.existsSync(absolutePath)) {
1474
1541
  return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
1475
1542
  }
1476
- const currentContent = fs11.readFileSync(absolutePath, "utf8");
1543
+ let diskContent = fs11.readFileSync(absolutePath, "utf8");
1544
+ if (diskContent.startsWith("\uFEFF")) {
1545
+ diskContent = diskContent.slice(1);
1546
+ }
1547
+ const normalizedDisk = diskContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1548
+ if (diskContent !== normalizedDisk) {
1549
+ fs11.writeFileSync(absolutePath, normalizedDisk, "utf8");
1550
+ diskContent = normalizedDisk;
1551
+ }
1552
+ const currentContent = diskContent;
1477
1553
  if (!currentContent.includes(content_to_replace)) {
1478
- return `ERROR: Could not find exact match for the specified "content_to_replace" in [${targetPath}]. Check indentation/whitespace/line breaks(LF or CRLF)/string. Try re-reading the file for latest changes.`;
1554
+ const diskLen = currentContent.length;
1555
+ const matchLen = content_to_replace.length;
1556
+ return `ERROR: Could not find exact match for the specified "content_to_replace" in [${targetPath}].
1557
+ - Disk Content Length (Normalized): ${diskLen}
1558
+ - Match String Length (Normalized): ${matchLen}
1559
+ - Check indentation/whitespace/line breaks. Try re-reading the file for latest changes.`;
1479
1560
  }
1480
1561
  const startPos = currentContent.indexOf(content_to_replace);
1481
1562
  const startLine = currentContent.substring(0, startPos).split(/\r?\n/).length;
@@ -1715,12 +1796,18 @@ var init_ask_user = __esm({
1715
1796
  import puppeteer3 from "puppeteer";
1716
1797
  import path13 from "path";
1717
1798
  import fs13 from "fs-extra";
1799
+ import { PDFDocument } from "pdf-lib";
1718
1800
  var write_pdf;
1719
1801
  var init_write_pdf = __esm({
1720
1802
  "src/tools/write_pdf.js"() {
1721
1803
  init_arg_parser();
1722
1804
  write_pdf = async (args) => {
1723
- const { path: targetPath, content, orientation = "portrait", margin = "10px" } = parseArgs(args);
1805
+ const {
1806
+ path: targetPath,
1807
+ content,
1808
+ orientation = "portrait",
1809
+ margin = "10px"
1810
+ } = parseArgs(args);
1724
1811
  if (!targetPath) return 'ERROR: Missing "path" argument for write_pdf.';
1725
1812
  if (!content) return 'ERROR: Missing "content" (HTML/CSS) for write_pdf.';
1726
1813
  const absolutePath = path13.resolve(process.cwd(), targetPath);
@@ -1757,7 +1844,7 @@ var init_write_pdf = __esm({
1757
1844
  font-weight: bold;
1758
1845
  color: rgba(0, 0, 0, 0.005);
1759
1846
  pointer-events: none;
1760
- z-index: 1000;
1847
+ z-index: -1000;
1761
1848
  text-align: center;
1762
1849
  width: 150%;
1763
1850
  white-space: nowrap;
@@ -1769,8 +1856,7 @@ var init_write_pdf = __esm({
1769
1856
  ${content}
1770
1857
  `;
1771
1858
  await page.setContent(styledContent, { waitUntil: "networkidle0" });
1772
- await page.pdf({
1773
- path: absolutePath,
1859
+ const pdfBytes = await page.pdf({
1774
1860
  format: "A4",
1775
1861
  landscape: orientation.toLowerCase() === "landscape",
1776
1862
  margin: {
@@ -1781,6 +1867,16 @@ var init_write_pdf = __esm({
1781
1867
  },
1782
1868
  printBackground: true
1783
1869
  });
1870
+ const pdfDoc = await PDFDocument.load(pdfBytes);
1871
+ const fileName = path13.basename(targetPath);
1872
+ pdfDoc.setTitle(`FluxFlow ${fileName}`);
1873
+ pdfDoc.setAuthor("FluxFlow CLI");
1874
+ pdfDoc.setSubject("Generated with AI");
1875
+ pdfDoc.setKeywords(["FluxFlow", "AI", "Agentic", "Automated"]);
1876
+ pdfDoc.setCreator("FluxFlow PDF Engine");
1877
+ pdfDoc.setProducer("FluxFlow (Generative AI)");
1878
+ const finalPdfBytes = await pdfDoc.save();
1879
+ await fs13.writeFile(absolutePath, finalPdfBytes);
1784
1880
  const stats = await fs13.stat(absolutePath);
1785
1881
  return `SUCCESS: PDF generated successfully at [${targetPath}] (${(stats.size / 1024).toFixed(2)} KB).`;
1786
1882
  } catch (err) {
@@ -1880,39 +1976,47 @@ var init_ai = __esm({
1880
1976
  };
1881
1977
  detectToolCalls = (text) => {
1882
1978
  const results = [];
1883
- const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(([\s\S]*?)\)(?:\s*\]?)/gi;
1979
+ const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(/gi;
1884
1980
  let match;
1885
1981
  while ((match = toolRegex.exec(text)) !== null) {
1886
- const fullMatch = match[0];
1887
1982
  const toolName = match[1];
1888
- const args = match[2];
1889
- let openCount = (args.match(/\(/g) || []).length;
1890
- let closeCount = (args.match(/\)/g) || []).length;
1891
- let finalArgs = args;
1892
- let finalFullMatch = fullMatch;
1893
- if (openCount > closeCount) {
1894
- const startIdx = match.index + fullMatch.indexOf("(");
1895
- let balance = 0;
1896
- let endIdx = -1;
1897
- for (let i = startIdx; i < text.length; i++) {
1898
- if (text[i] === "(") balance++;
1899
- if (text[i] === ")") balance--;
1983
+ const startIdx = match.index + match[0].length - 1;
1984
+ let balance = 0;
1985
+ let inString = null;
1986
+ let isEscaped = false;
1987
+ let endIdx = -1;
1988
+ for (let i = startIdx; i < text.length; i++) {
1989
+ const char = text[i];
1990
+ if (!inString && (char === '"' || char === "'" || char === "`")) {
1991
+ inString = char;
1992
+ isEscaped = false;
1993
+ } else if (inString && char === inString && !isEscaped) {
1994
+ inString = null;
1995
+ }
1996
+ if (!inString) {
1997
+ if (char === "(") balance++;
1998
+ else if (char === ")") balance--;
1900
1999
  if (balance === 0) {
1901
2000
  endIdx = i;
1902
2001
  break;
1903
2002
  }
1904
2003
  }
1905
- if (endIdx !== -1) {
1906
- finalArgs = text.substring(startIdx + 1, endIdx);
1907
- finalFullMatch = text.substring(match.index, endIdx + 1);
1908
- toolRegex.lastIndex = endIdx + 1;
2004
+ if (char === "\\") {
2005
+ isEscaped = !isEscaped;
2006
+ } else {
2007
+ isEscaped = false;
1909
2008
  }
1910
2009
  }
1911
- results.push({
1912
- fullMatch: finalFullMatch,
1913
- toolName: toolName.trim(),
1914
- args: finalArgs.trim()
1915
- });
2010
+ if (endIdx !== -1) {
2011
+ const finalArgs = text.substring(startIdx + 1, endIdx);
2012
+ const finalFullMatch = text.substring(match.index, endIdx + 1);
2013
+ results.push({
2014
+ fullMatch: finalFullMatch,
2015
+ toolName: toolName.trim(),
2016
+ args: finalArgs.trim()
2017
+ });
2018
+ toolRegex.lastIndex = endIdx + 1;
2019
+ }
1916
2020
  }
1917
2021
  return results;
1918
2022
  };
@@ -1948,7 +2052,7 @@ USER_PROMPT: ${agentText}`.trim();
1948
2052
  let lastUsage = null;
1949
2053
  const MAX_LOOPS = mode === "Flux" ? 50 : 7;
1950
2054
  const MAX_RETRIES = 7;
1951
- yield { type: "status", content: "Working..." };
2055
+ yield { type: "status", content: "Connecting..." };
1952
2056
  TERMINATION_SIGNAL = false;
1953
2057
  let fullAgentResponseChunks = [];
1954
2058
  modifiedHistory.forEach((msg) => {
@@ -2051,6 +2155,7 @@ USER_PROMPT: ${agentText}`.trim();
2051
2155
  lastUsage = chunk.usageMetadata;
2052
2156
  if (lastUsage) {
2053
2157
  yield { type: "liveTokens", content: lastUsage.totalTokenCount };
2158
+ yield { type: "status", content: "Working..." };
2054
2159
  }
2055
2160
  }
2056
2161
  await incrementUsage("agent");
@@ -2058,7 +2163,11 @@ USER_PROMPT: ${agentText}`.trim();
2058
2163
  yield { type: "usage", content: lastUsage };
2059
2164
  }
2060
2165
  fullAgentResponseChunks.push(turnText);
2061
- const textToProcess = turnText.replace(/<think>[\s\S]*?<\/think>/g, "");
2166
+ let textToProcess = turnText;
2167
+ const thinkMatch = turnText.match(/<think>([\s\S]*?)<\/think>/i);
2168
+ if (thinkMatch) {
2169
+ textToProcess = turnText.replace(/<think>[\s\S]*?<\/think>/i, "");
2170
+ }
2062
2171
  const turnTextLower = textToProcess.toLowerCase();
2063
2172
  const hasFinish = /\[\s*(turn\s*:)?\s*finish\s*\]/i.test(turnTextLower);
2064
2173
  const toolCalls = detectToolCalls(textToProcess);
@@ -2419,13 +2528,13 @@ var init_settings = __esm({
2419
2528
  });
2420
2529
 
2421
2530
  // src/components/ResumeModal.jsx
2422
- import React7, { useState as useState3, useEffect } from "react";
2531
+ import React7, { useState as useState4, useEffect as useEffect2 } from "react";
2423
2532
  import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
2424
2533
  function ResumeModal({ onSelect, onDelete, onClose }) {
2425
- const [history, setHistory] = useState3({});
2426
- const [keys, setKeys] = useState3([]);
2427
- const [selectedIndex, setSelectedIndex] = useState3(0);
2428
- useEffect(() => {
2534
+ const [history, setHistory] = useState4({});
2535
+ const [keys, setKeys] = useState4([]);
2536
+ const [selectedIndex, setSelectedIndex] = useState4(0);
2537
+ useEffect2(() => {
2429
2538
  const fetchHistory = async () => {
2430
2539
  const h = await loadHistory();
2431
2540
  setHistory(h);
@@ -2462,16 +2571,16 @@ var init_ResumeModal = __esm({
2462
2571
  });
2463
2572
 
2464
2573
  // src/components/MemoryModal.jsx
2465
- import React8, { useState as useState4, useEffect as useEffect2 } from "react";
2574
+ import React8, { useState as useState5, useEffect as useEffect3 } from "react";
2466
2575
  import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
2467
2576
  function MemoryModal({ onClose }) {
2468
- const [memories, setMemories] = useState4([]);
2469
- const [selectedIndex, setSelectedIndex] = useState4(0);
2577
+ const [memories, setMemories] = useState5([]);
2578
+ const [selectedIndex, setSelectedIndex] = useState5(0);
2470
2579
  const loadMemories = () => {
2471
2580
  const data = readEncryptedJson(MEMORIES_FILE, []);
2472
2581
  setMemories(data);
2473
2582
  };
2474
- useEffect2(() => {
2583
+ useEffect3(() => {
2475
2584
  loadMemories();
2476
2585
  }, []);
2477
2586
  useInput3((input, key) => {
@@ -2501,7 +2610,7 @@ var init_MemoryModal = __esm({
2501
2610
  });
2502
2611
 
2503
2612
  // src/components/UpdateProcessor.jsx
2504
- import React9, { useState as useState5, useEffect as useEffect3 } from "react";
2613
+ import React9, { useState as useState6, useEffect as useEffect4 } from "react";
2505
2614
  import { Box as Box9, Text as Text9 } from "ink";
2506
2615
  import Spinner from "ink-spinner";
2507
2616
  import { exec } from "child_process";
@@ -2509,10 +2618,10 @@ var UpdateProcessor, UpdateProcessor_default;
2509
2618
  var init_UpdateProcessor = __esm({
2510
2619
  "src/components/UpdateProcessor.jsx"() {
2511
2620
  UpdateProcessor = ({ latest, current, settings, onClose, onUpdateSettings, onSuccess }) => {
2512
- const [status, setStatus] = useState5("initializing");
2513
- const [log, setLog] = useState5("");
2514
- const [error, setError] = useState5(null);
2515
- useEffect3(() => {
2621
+ const [status, setStatus] = useState6("initializing");
2622
+ const [log, setLog] = useState6("");
2623
+ const [error, setError] = useState6(null);
2624
+ useEffect4(() => {
2516
2625
  const runUpdate = async () => {
2517
2626
  const manager = settings.updateManager || "npm";
2518
2627
  if (!settings.updateManager) {
@@ -2599,7 +2708,7 @@ var app_exports = {};
2599
2708
  __export(app_exports, {
2600
2709
  default: () => App
2601
2710
  });
2602
- import React10, { useState as useState6, useEffect as useEffect4, useRef, useMemo } from "react";
2711
+ import React10, { useState as useState7, useEffect as useEffect5, useRef as useRef2, useMemo } from "react";
2603
2712
  import { Box as Box10, Text as Text10, useInput as useInput4, useStdout } from "ink";
2604
2713
  import Spinner2 from "ink-spinner";
2605
2714
  import fs17 from "fs-extra";
@@ -2609,14 +2718,14 @@ import TextInput3 from "ink-text-input";
2609
2718
  import gradient from "gradient-string";
2610
2719
  function App() {
2611
2720
  const { stdout } = useStdout();
2612
- const [input, setInput] = useState6("");
2613
- const [isExpanded, setIsExpanded] = useState6(false);
2614
- const [mode, setMode] = useState6("Flux");
2615
- const [terminalSize, setTerminalSize] = useState6({
2721
+ const [input, setInput] = useState7("");
2722
+ const [isExpanded, setIsExpanded] = useState7(false);
2723
+ const [mode, setMode] = useState7("Flux");
2724
+ const [terminalSize, setTerminalSize] = useState7({
2616
2725
  columns: stdout?.columns || 80,
2617
2726
  rows: stdout?.rows || 24
2618
2727
  });
2619
- const [selectedIndex, setSelectedIndex] = useState6(0);
2728
+ const [selectedIndex, setSelectedIndex] = useState7(0);
2620
2729
  const performVersionCheck = async (manual = false, settingsOverride = null) => {
2621
2730
  const settingsToUse = settingsOverride || systemSettings;
2622
2731
  if (manual) {
@@ -2640,7 +2749,7 @@ function App() {
2640
2749
  id: "update-" + Date.now(),
2641
2750
  role: "system",
2642
2751
  text: `\u{1F680} **New version 'v${latestVersion}' is available!**
2643
- Type \`/update\` to upgrade immediately.
2752
+ Type \`/update latest\` to upgrade immediately.
2644
2753
  Check what's new using \`/changelog\` command.`,
2645
2754
  isUpdateNotification: true,
2646
2755
  isMeta: true
@@ -2663,7 +2772,7 @@ Check what's new using \`/changelog\` command.`,
2663
2772
  }
2664
2773
  }
2665
2774
  };
2666
- useEffect4(() => {
2775
+ useEffect5(() => {
2667
2776
  const handleResize = () => {
2668
2777
  stdout.write("\x1Bc");
2669
2778
  setTerminalSize({
@@ -2676,29 +2785,29 @@ Check what's new using \`/changelog\` command.`,
2676
2785
  stdout.off("resize", handleResize);
2677
2786
  };
2678
2787
  }, [stdout]);
2679
- const [thinkingLevel, setThinkingLevel] = useState6("Medium");
2680
- const [latestVer, setLatestVer] = useState6(null);
2681
- const [showFullThinking, setShowFullThinking] = useState6(false);
2682
- const [activeModel, setActiveModel] = useState6("gemma-4-31b-it");
2683
- const [janitorModel, setJanitorModel] = useState6("gemma-4-26b-a4b-it");
2684
- const [isInitializing, setIsInitializing] = useState6(true);
2685
- const [apiKey, setApiKey] = useState6(null);
2686
- const [tempKey, setTempKey] = useState6("");
2687
- const [activeView, setActiveView] = useState6("chat");
2688
- const [apiTier, setApiTier] = useState6("Free");
2689
- const [quotas, setQuotas] = useState6({ agentLimit: 1500, backgroundLimit: 1500, searchLimit: 100, customModelId: "", customLimit: 0 });
2690
- const [inputConfig, setInputConfig] = useState6(null);
2691
- const [systemSettings, setSystemSettings] = useState6({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
2692
- const [profileData, setProfileData] = useState6({ name: null, nickname: null, instructions: null });
2693
- const [sessionStats, setSessionStats] = useState6({ tokens: 0 });
2694
- const [sessionAgentCalls, setSessionAgentCalls] = useState6(0);
2695
- const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState6(0);
2696
- const [sessionTotalTokens, setSessionTotalTokens] = useState6(0);
2697
- const [dailyUsage, setDailyUsage] = useState6(null);
2698
- const [chatId, setChatId] = useState6(generateChatId());
2699
- const [activeCommand, setActiveCommand] = useState6(null);
2700
- const [execOutput, setExecOutput] = useState6("");
2701
- const [isTerminalFocused, setIsTerminalFocused] = useState6(false);
2788
+ const [thinkingLevel, setThinkingLevel] = useState7("Medium");
2789
+ const [latestVer, setLatestVer] = useState7(null);
2790
+ const [showFullThinking, setShowFullThinking] = useState7(false);
2791
+ const [activeModel, setActiveModel] = useState7("gemma-4-31b-it");
2792
+ const [janitorModel, setJanitorModel] = useState7("gemma-4-26b-a4b-it");
2793
+ const [isInitializing, setIsInitializing] = useState7(true);
2794
+ const [apiKey, setApiKey] = useState7(null);
2795
+ const [tempKey, setTempKey] = useState7("");
2796
+ const [activeView, setActiveView] = useState7("chat");
2797
+ const [apiTier, setApiTier] = useState7("Free");
2798
+ const [quotas, setQuotas] = useState7({ agentLimit: 1500, backgroundLimit: 1500, searchLimit: 100, customModelId: "", customLimit: 0 });
2799
+ const [inputConfig, setInputConfig] = useState7(null);
2800
+ const [systemSettings, setSystemSettings] = useState7({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
2801
+ const [profileData, setProfileData] = useState7({ name: null, nickname: null, instructions: null });
2802
+ const [sessionStats, setSessionStats] = useState7({ tokens: 0 });
2803
+ const [sessionAgentCalls, setSessionAgentCalls] = useState7(0);
2804
+ const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState7(0);
2805
+ const [sessionTotalTokens, setSessionTotalTokens] = useState7(0);
2806
+ const [dailyUsage, setDailyUsage] = useState7(null);
2807
+ const [chatId, setChatId] = useState7(generateChatId());
2808
+ const [activeCommand, setActiveCommand] = useState7(null);
2809
+ const [execOutput, setExecOutput] = useState7("");
2810
+ const [isTerminalFocused, setIsTerminalFocused] = useState7(false);
2702
2811
  const terminalEnv = useMemo(() => {
2703
2812
  const isIDE = process.env.TERM_PROGRAM === "vscode" || !!process.env.VSC_TERMINAL_URL || !!process.env.INTELLIJ_TERMINAL_COMMAND_BLOCKS;
2704
2813
  return {
@@ -2706,17 +2815,17 @@ Check what's new using \`/changelog\` command.`,
2706
2815
  shortcut: isIDE ? "Shift + Enter" : "Ctrl + Enter"
2707
2816
  };
2708
2817
  }, []);
2709
- const activeCommandRef = useRef(null);
2710
- const execOutputRef = useRef("");
2711
- useEffect4(() => {
2818
+ const activeCommandRef = useRef2(null);
2819
+ const execOutputRef = useRef2("");
2820
+ useEffect5(() => {
2712
2821
  activeCommandRef.current = activeCommand;
2713
2822
  }, [activeCommand]);
2714
- useEffect4(() => {
2823
+ useEffect5(() => {
2715
2824
  execOutputRef.current = execOutput;
2716
2825
  }, [execOutput]);
2717
- const [autoAcceptWrites, setAutoAcceptWrites] = useState6(false);
2718
- const [pendingApproval, setPendingApproval] = useState6(null);
2719
- const [pendingAsk, setPendingAsk] = useState6(null);
2826
+ const [autoAcceptWrites, setAutoAcceptWrites] = useState7(false);
2827
+ const [pendingApproval, setPendingApproval] = useState7(null);
2828
+ const [pendingAsk, setPendingAsk] = useState7(null);
2720
2829
  const formatDuration = (totalSecs) => {
2721
2830
  const h = Math.floor(totalSecs / 3600);
2722
2831
  const m = Math.floor(totalSecs % 3600 / 60);
@@ -2727,18 +2836,18 @@ Check what's new using \`/changelog\` command.`,
2727
2836
  parts.push(`${s}s`);
2728
2837
  return parts.join(" ");
2729
2838
  };
2730
- const [statusText, setStatusText] = useState6(null);
2731
- const [isProcessing, setIsProcessing] = useState6(false);
2732
- const [escPressed, setEscPressed] = useState6(false);
2733
- const [escTimer, setEscTimer] = useState6(null);
2734
- const [queuedPrompt, setQueuedPrompt] = useState6(null);
2735
- const [resolutionData, setResolutionData] = useState6(null);
2736
- const [tempModelOverride, setTempModelOverride] = useState6(null);
2737
- const [messages, setMessages] = useState6([
2839
+ const [statusText, setStatusText] = useState7(null);
2840
+ const [isProcessing, setIsProcessing] = useState7(false);
2841
+ const [escPressed, setEscPressed] = useState7(false);
2842
+ const [escTimer, setEscTimer] = useState7(null);
2843
+ const [queuedPrompt, setQueuedPrompt] = useState7(null);
2844
+ const [resolutionData, setResolutionData] = useState7(null);
2845
+ const [tempModelOverride, setTempModelOverride] = useState7(null);
2846
+ const [messages, setMessages] = useState7([
2738
2847
  { id: "welcome", role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome to Flux Flow! Type /help for commands.\n", isMeta: true }
2739
2848
  ]);
2740
- const queuedPromptRef = useRef(null);
2741
- const [completedIndex, setCompletedIndex] = useState6(1);
2849
+ const queuedPromptRef = useRef2(null);
2850
+ const [completedIndex, setCompletedIndex] = useState7(1);
2742
2851
  const windowedHistory = useMemo(() => {
2743
2852
  const MAX_LINES = 1e3;
2744
2853
  const width = stdout?.columns || 80;
@@ -2840,7 +2949,7 @@ Check what's new using \`/changelog\` command.`,
2840
2949
  setInput((prev) => prev.replace(/\\\r?$/, "").replace(/\r?$/, "") + "\n");
2841
2950
  }
2842
2951
  });
2843
- useEffect4(() => {
2952
+ useEffect5(() => {
2844
2953
  async function init() {
2845
2954
  if (!checkPuppeteerReady()) {
2846
2955
  setMessages((prev) => {
@@ -2885,7 +2994,7 @@ Check what's new using \`/changelog\` command.`,
2885
2994
  }
2886
2995
  init();
2887
2996
  }, []);
2888
- useEffect4(() => {
2997
+ useEffect5(() => {
2889
2998
  if (!isInitializing) {
2890
2999
  saveSettings({
2891
3000
  mode,
@@ -2944,7 +3053,7 @@ Check what's new using \`/changelog\` command.`,
2944
3053
  { cmd: "/changelog", desc: "View latest updates" },
2945
3054
  { cmd: "/update", desc: "Check/Install updates", subs: [
2946
3055
  { cmd: "check", desc: "Check for new version" },
2947
- { cmd: "force", desc: "Force reinstall latest" }
3056
+ { cmd: "latest", desc: "Install Latest Version" }
2948
3057
  ] }
2949
3058
  ];
2950
3059
  const handleSubmit = (value) => {
@@ -3197,7 +3306,7 @@ ${list || "No saved chats found."}`, isMeta: true }];
3197
3306
  performVersionCheck(true);
3198
3307
  break;
3199
3308
  }
3200
- const isForce = parts.includes("--force");
3309
+ const isForce = parts.includes("--latest");
3201
3310
  setActiveView("update");
3202
3311
  break;
3203
3312
  }
@@ -3330,6 +3439,12 @@ Selection: ${val}`,
3330
3439
  let inThinkMode = false;
3331
3440
  let currentThinkId = null;
3332
3441
  let currentAgentId = null;
3442
+ let inCodeBlock = false;
3443
+ let inToolCall = false;
3444
+ let thinkConsumedInTurn = false;
3445
+ let toolCallEncounteredInTurn = false;
3446
+ let toolCallBalance = 0;
3447
+ let inToolCallString = null;
3333
3448
  const signalRegex = /\[?_DISABLED_SIGNAL_REGEX_\]?/gi;
3334
3449
  for await (const packet of stream) {
3335
3450
  if (packet.type === "status") {
@@ -3344,6 +3459,9 @@ Selection: ${val}`,
3344
3459
  currentThinkId = null;
3345
3460
  currentAgentId = null;
3346
3461
  inThinkMode = false;
3462
+ inCodeBlock = false;
3463
+ inToolCall = false;
3464
+ toolCallEncounteredInTurn = false;
3347
3465
  continue;
3348
3466
  }
3349
3467
  if (packet.type === "memory_updated") {
@@ -3396,23 +3514,50 @@ Selection: ${val}`,
3396
3514
  continue;
3397
3515
  }
3398
3516
  let chunkText = packet.content;
3399
- if ((chunkText.toLowerCase().includes("<think") || chunkText.toLowerCase().includes("<thought")) && !inThinkMode) {
3517
+ const chunkLower = chunkText.toLowerCase();
3518
+ if (chunkText.includes("```")) inCodeBlock = !inCodeBlock;
3519
+ if (chunkLower.includes("tool:functions.")) {
3520
+ inToolCall = true;
3521
+ toolCallBalance = 0;
3522
+ inToolCallString = null;
3523
+ }
3524
+ if (inToolCall) {
3525
+ for (let j = 0; j < chunkText.length; j++) {
3526
+ const char = chunkText[j];
3527
+ if (!inToolCallString && (char === "'" || char === '"' || char === "`")) {
3528
+ inToolCallString = char;
3529
+ } else if (inToolCallString && char === inToolCallString && chunkText[j - 1] !== "\\") {
3530
+ inToolCallString = null;
3531
+ }
3532
+ if (!inToolCallString) {
3533
+ if (char === "(") toolCallBalance++;
3534
+ else if (char === ")") toolCallBalance--;
3535
+ }
3536
+ }
3537
+ if (toolCallBalance <= 0 && !inToolCallString) {
3538
+ inToolCall = false;
3539
+ }
3540
+ }
3541
+ const hasThinkTag = chunkLower.includes("<think") || chunkLower.includes("<thought");
3542
+ const canThink = !inThinkMode && !inCodeBlock && !inToolCall && !thinkConsumedInTurn;
3543
+ if (hasThinkTag && canThink) {
3400
3544
  inThinkMode = true;
3401
- chunkText = chunkText.replace(/<(think|thought)>/gi, "");
3545
+ thinkConsumedInTurn = true;
3546
+ chunkText = chunkText.replace(/<(think|thought)>[\s\S]*?<\/(think|thought)>/gi, "").replace(/<(think|thought)>/gi, "");
3402
3547
  currentThinkId = "think-" + Date.now();
3403
- setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: "" }]);
3548
+ setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: "", isStreaming: true }]);
3404
3549
  }
3405
- if (chunkText.toLowerCase().includes("</think>") || chunkText.toLowerCase().includes("</thought>")) {
3550
+ if (chunkLower.includes("</think>") || chunkLower.includes("</thought>")) {
3406
3551
  const parts = chunkText.split(/<\/(think|thought)>/gi);
3407
3552
  const thinkPart = parts[0] || "";
3408
- const agentPart = parts.slice(2).join("") || "";
3553
+ const agentPart = parts.slice(2).join("").replace(/<\/?(think|thought)>/gi, "");
3409
3554
  setMessages((prev) => {
3410
3555
  const newMsgs = prev.map(
3411
- (m) => m.id === currentThinkId ? { ...m, text: m.text + thinkPart } : m
3556
+ (m) => m.id === currentThinkId ? { ...m, text: m.text + thinkPart, isStreaming: true } : m
3412
3557
  );
3413
3558
  inThinkMode = false;
3414
3559
  currentAgentId = "agent-" + Date.now();
3415
- return [...newMsgs, { id: currentAgentId, role: "agent", text: agentPart.replace(/<\/?(think|thought)>/gi, "") }];
3560
+ return [...newMsgs, { id: currentAgentId, role: "agent", text: agentPart, isStreaming: true }];
3416
3561
  });
3417
3562
  continue;
3418
3563
  }
@@ -3429,26 +3574,33 @@ Selection: ${val}`,
3429
3574
  transitionContent = parts.slice(1).join("</think>") || "";
3430
3575
  return { ...m, text: parts[0] };
3431
3576
  }
3432
- return { ...m, text: newText };
3577
+ return { ...m, text: newText, isStreaming: true };
3433
3578
  }
3434
3579
  return m;
3435
3580
  });
3436
3581
  if (transitioning) {
3437
3582
  inThinkMode = false;
3438
3583
  currentAgentId = "agent-" + Date.now();
3439
- return [...newMsgs, { id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?think>/gi, "") }];
3584
+ return [...newMsgs, { id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?(think|thought)>/gi, ""), isStreaming: true }];
3440
3585
  }
3441
3586
  return newMsgs;
3442
3587
  });
3443
- } else if (!inThinkMode) {
3444
- const cleanedText = chunkText.replace(/<\/?(think|thought)>/gi, "").replace(signalRegex, "");
3445
- if (!currentAgentId) {
3446
- currentAgentId = "agent-" + Date.now();
3447
- setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: cleanedText }]);
3448
- } else {
3449
- setMessages((prev) => prev.map(
3450
- (m) => m.id === currentAgentId ? { ...m, text: m.text + cleanedText } : m
3451
- ));
3588
+ } else if (!inThinkMode && !toolCallEncounteredInTurn) {
3589
+ let cleanedText = chunkText.replace(/<(think|thought)>[\s\S]*?<\/(think|thought)>/gi, "").replace(/<\/?(think|thought)>/gi, "").replace(signalRegex, "");
3590
+ const toolIdx = cleanedText.toLowerCase().indexOf("tool:functions.");
3591
+ if (toolIdx !== -1) {
3592
+ cleanedText = cleanedText.substring(0, toolIdx);
3593
+ toolCallEncounteredInTurn = true;
3594
+ }
3595
+ if (cleanedText) {
3596
+ if (!currentAgentId) {
3597
+ currentAgentId = "agent-" + Date.now();
3598
+ setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: cleanedText, isStreaming: true }]);
3599
+ } else {
3600
+ setMessages((prev) => prev.map(
3601
+ (m) => m.id === currentAgentId ? { ...m, text: m.text + cleanedText, isStreaming: true } : m
3602
+ ));
3603
+ }
3452
3604
  }
3453
3605
  }
3454
3606
  }
@@ -3476,10 +3628,11 @@ Selection: ${val}`,
3476
3628
  setActiveView("resolution");
3477
3629
  }
3478
3630
  setMessages((prev) => {
3479
- const historyToSave = prev.filter((m) => !String(m.id).startsWith("welcome") && !m.isVisualFeedback);
3631
+ const newMsgs = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
3632
+ const historyToSave = newMsgs.filter((m) => !String(m.id).startsWith("welcome") && !m.isVisualFeedback);
3480
3633
  saveChat(chatId, null, historyToSave);
3481
- setCompletedIndex(prev.length);
3482
- return prev;
3634
+ setCompletedIndex(newMsgs.length);
3635
+ return newMsgs;
3483
3636
  });
3484
3637
  }
3485
3638
  };
@@ -3507,7 +3660,7 @@ Selection: ${val}`,
3507
3660
  }
3508
3661
  return [];
3509
3662
  }, [input]);
3510
- useEffect4(() => {
3663
+ useEffect5(() => {
3511
3664
  setSelectedIndex(0);
3512
3665
  }, [suggestions]);
3513
3666
  const renderActiveView = () => {
@@ -4145,7 +4298,7 @@ var init_app = __esm({
4145
4298
  init_setup();
4146
4299
  SESSION_START_TIME = Date.now();
4147
4300
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
4148
- versionFluxflow = "1.5.4";
4301
+ versionFluxflow = "1.6.1";
4149
4302
  updatedOn = "2026-05-02";
4150
4303
  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(
4151
4304
  CommandMenu,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.5.4",
3
+ "version": "1.6.1",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "scripts": {
36
36
  "start": "tsx ./src/cli.jsx",
37
- "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:ink-spinner --external:ink-multiline-input --external:@google/genai --external:zod --external:nanoid --external:puppeteer --external:typescript"
37
+ "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:ink-spinner --external:ink-multiline-input --external:@google/genai --external:zod --external:nanoid --external:puppeteer --external:pdf-lib --external:typescript"
38
38
  },
39
39
  "dependencies": {
40
40
  "@google/genai": "^1.50.1",
@@ -47,6 +47,7 @@
47
47
  "ink-spinner": "^5.0.0",
48
48
  "ink-text-input": "^6.0.0",
49
49
  "nanoid": "^5.1.9",
50
+ "pdf-lib": "^1.17.1",
50
51
  "puppeteer": "^24.42.0",
51
52
  "react": "^19.2.5",
52
53
  "zod": "^4.3.6"