fluxflow-cli 1.0.1 → 1.0.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kushal Roy Chowdhury
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/TOOLS.md ADDED
@@ -0,0 +1,48 @@
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
+ | **View/Read Files** | ✅ | ❌ |
12
+ | **Write/Update Files** | ✅ | ❌ |
13
+ | **Execute Commands** | ✅ | ❌ |
14
+
15
+ ---
16
+
17
+ ## Core Tools
18
+
19
+ ### 🌐 Web & Research
20
+ - **`web_search`**: Uses DuckDuckGo to find up-to-date information on the internet. Crucial for answering questions about recent events or unlearned documentation.
21
+ - **`web_scrape`**: Extracts the detailed text content from a specific URL, allowing the agent to read documentation or articles.
22
+
23
+ ### 📁 File System Operations
24
+ - **`list_files`**: Lists the contents of a directory to help the agent understand the project structure.
25
+ - **`read_folder`**: Provides detailed statistics and metadata about a directory's contents.
26
+ - **`view_file`**: Reads the content of a file. Supports reading specific line ranges (`start_line`, `end_line`) to manage context size efficiently.
27
+
28
+ ### ✍️ Code Editing
29
+ - **`write_file`**: Creates a new file or completely overwrites an existing one with new content.
30
+ - **`update_file` (Smart Patching)**: Surgically replaces a specific block of text within a file.
31
+ - *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.
32
+
33
+ ### 💻 Terminal Execution
34
+ - **`exec_command`**: Runs a shell command directly in the terminal using Node's `child_process.spawn`.
35
+ - *Context Aware*: Runs in the current working directory.
36
+ - *Cross-Platform*: Uses `shell: true` to handle Windows `.cmd`/`.bat` files natively.
37
+
38
+ ---
39
+
40
+ ## Memory Management
41
+
42
+ The memory tool (`memory.js`) is primarily used by the background **Janitor** model, but can be accessed by the main agent if necessary.
43
+
44
+ - **Temporary Context (`action='temp'`)**: Saves a rolling summary of the current session to maintain conversational context without bloating the main prompt history.
45
+ - **Persistent User Memory (`action='user'`)**:
46
+ - The Janitor analyzes conversations to detect user preferences, hobbies, or instructions.
47
+ - It uses `add`, `update`, or `delete` methods to manage facts in the encrypted `memories.json` vault.
48
+ - 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 ADDED
@@ -0,0 +1,66 @@
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
+ - **/memory**: View and manage the persistent memories extracted by the Janitor.
16
+ - **/resume <chat-id>**: Switch back to a previous conversation.
17
+ - **/help**: List all available commands.
18
+
19
+ ### Command Shortcuts
20
+
21
+ For power users, several commands support direct arguments to skip the menus:
22
+ - ` /mode flux ` or ` /mode flow `
23
+ - ` /thinking low ` / ` /thinking medium ` / ` /thinking high ` / ` /thinking max `
24
+ - ` /thinking show ` / ` /thinking hide ` (Toggles thinking process visibility)
25
+ - ` /model gemini-3.1-pro-preview ` (Switches model directly)
26
+
27
+ ## 🧠 Thinking Levels & Visualization
28
+
29
+ Flux Flow separates the model's "internal monologue" (reasoning) from its final response using `<think>` tags.
30
+
31
+ - **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.
32
+ - **Show/Hide Thinking**: You can toggle the visibility of the thinking process using `/thinking show/hide`.
33
+ - 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.
34
+
35
+ ## 🛡️ Human-in-the-Loop (HITL) Verification
36
+
37
+ Security and safety are paramount when an AI has access to your file system and terminal. Flux Flow implements several layers of verification:
38
+
39
+ ### Tool Approval
40
+ By default, the agent **cannot** execute dangerous actions without your consent.
41
+ - **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.
42
+ - **File Approval**: Similar to terminal commands, writing or updating files requires manual approval unless configured otherwise.
43
+ - **Safe Commands**: Basic read-only commands (like `ls`, `git status`, `pwd`) are automatically allowed to minimize friction.
44
+
45
+ ### Auto-Execution (Advanced)
46
+ For power users, **Auto-Exec** can be enabled in `/settings`.
47
+ - **⚠️ Warning**: This allows the agent to run any tool and execute any command autonomously.
48
+ - **External Access**: You can also toggle whether the agent is allowed to access files outside of its current working directory.
49
+
50
+ ## 🔄 Steering & Resolution
51
+
52
+ ### Real-time Steering
53
+ 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.
54
+
55
+ ### Resolution Modal
56
+ If the agent finishes its task just as you send a steering hint, a **Resolution Modal** appears. It asks if you want to:
57
+ - **Send Anyway**: Start a new loop with your feedback.
58
+ - **Edit Prompt**: Refine your feedback before sending.
59
+
60
+ ## 📊 Status Bar & Feedback
61
+ The bottom of the screen features a dynamic status bar showing:
62
+ - **Active Mode** (Flux/Flow)
63
+ - **Thinking Level**
64
+ - **Token Usage**: Real-time tracking of tokens used in the current session.
65
+ - **Agentic Loops**: Counters showing how many times the agent has "looped" to solve the current task.
66
+ - **API Status**: Visual feedback when the model is thinking or executing a tool.
@@ -8,8 +8,6 @@ import { render } from "ink";
8
8
  import React8, { useState as useState4, useEffect as useEffect3, useRef, useMemo } from "react";
9
9
  import { Box as Box8, Text as Text8, useInput as useInput3, useStdout, Static } from "ink";
10
10
  import fs14 from "fs-extra";
11
- import path16 from "path";
12
- import { fileURLToPath as fileURLToPath9 } from "url";
13
11
  import { MultilineInput } from "ink-multiline-input";
14
12
  import TextInput2 from "ink-text-input";
15
13
 
@@ -194,7 +192,7 @@ function StatusBar({ mode, thinkingLevel, tokens = "0.0k", chatId = "NEW-SESSION
194
192
  },
195
193
  /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: modeColor, bold: true }, modeIcon, " ", mode.toUpperCase()), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "magenta" }, "\u{1F9E0} ", thinkingLevel)),
196
194
  /* @__PURE__ */ React3.createElement(Box3, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "\u{1F4C1} "), /* @__PURE__ */ React3.createElement(Text3, { color: "blue", dimColor: true, italic: true }, process.cwd())),
197
- /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, "MEM: "), /* @__PURE__ */ React3.createElement(Text3, { color: memStatus === "ON" ? "green" : "red" }, memStatus), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, " Tokens ", tokens > 1e3 ? `${(tokens / 1e3).toFixed(1)}k` : tokens, " (", Math.round(tokens / 128e3 * 100), "%)"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "dim" }, "ID: ", chatId, " "))
195
+ /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, "MEM: "), /* @__PURE__ */ React3.createElement(Text3, { color: memStatus === "ON" ? "green" : "red" }, memStatus), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, " Tokens ", tokens > 1e3 ? `${(tokens / 1e3).toFixed(1)}k` : tokens, " (", Math.round(tokens / 196e3 * 100), "%)"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "dim" }, "ID: ", chatId, " "))
198
196
  );
199
197
  }
200
198
 
@@ -257,8 +255,6 @@ import gradient from "gradient-string";
257
255
 
258
256
  // src/utils/secrets.js
259
257
  import fs2 from "fs-extra";
260
- import path2 from "path";
261
- import { fileURLToPath } from "url";
262
258
 
263
259
  // src/utils/crypto.js
264
260
  import fs from "fs";
@@ -295,12 +291,21 @@ var writeEncryptedJson = (filePath, data) => {
295
291
  }
296
292
  };
297
293
 
294
+ // src/utils/paths.js
295
+ import os from "os";
296
+ import path2 from "path";
297
+ var FLUXFLOW_DIR = path2.join(os.homedir(), ".fluxflow");
298
+ var LOGS_DIR = path2.join(FLUXFLOW_DIR, "logs");
299
+ var SECRET_DIR = path2.join(FLUXFLOW_DIR, "secret");
300
+ var SETTINGS_FILE = path2.join(FLUXFLOW_DIR, "settings.json");
301
+ var HISTORY_FILE = path2.join(SECRET_DIR, "history.json");
302
+ var USAGE_FILE = path2.join(SECRET_DIR, "usage.json");
303
+ var MEMORIES_FILE = path2.join(SECRET_DIR, "memories.json");
304
+ var TEMP_MEM_FILE = path2.join(SECRET_DIR, "memory-temp.json");
305
+
298
306
  // src/utils/secrets.js
299
- var __filename = fileURLToPath(import.meta.url);
300
- var __dirname = path2.dirname(__filename);
301
- var AGENT_ROOT = path2.join(__dirname, "../../");
302
- var SECRET_DIR = path2.join(AGENT_ROOT, "secret");
303
- var SECRET_FILE = path2.join(SECRET_DIR, "secrets.json");
307
+ import path3 from "path";
308
+ var SECRET_FILE = path3.join(SECRET_DIR, "secrets.json");
304
309
  var getAPIKey = async () => {
305
310
  try {
306
311
  const secrets = readEncryptedJson(SECRET_FILE, {});
@@ -476,13 +481,8 @@ Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString()}
476
481
 
477
482
  // src/utils/history.js
478
483
  import fs3 from "fs-extra";
479
- import path3 from "path";
480
- import { fileURLToPath as fileURLToPath2 } from "url";
484
+ import path4 from "path";
481
485
  import { nanoid } from "nanoid";
482
- var __filename2 = fileURLToPath2(import.meta.url);
483
- var __dirname2 = path3.dirname(__filename2);
484
- var AGENT_ROOT2 = path3.join(__dirname2, "../../");
485
- var HISTORY_FILE = path3.join(AGENT_ROOT2, "secret/history.json");
486
486
  var WRITE_LOCK = Promise.resolve();
487
487
  var withLock = (op) => {
488
488
  const nextLock = WRITE_LOCK.then(async () => {
@@ -517,7 +517,7 @@ var saveChat = async (id, name, messages) => {
517
517
  messages,
518
518
  updatedAt: Date.now()
519
519
  };
520
- await fs3.ensureDir(path3.dirname(HISTORY_FILE));
520
+ await fs3.ensureDir(path4.dirname(HISTORY_FILE));
521
521
  await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
522
522
  });
523
523
  };
@@ -530,7 +530,7 @@ var saveChatTitle = async (id, title) => {
530
530
  } else {
531
531
  history[id] = { name: title, messages: [], updatedAt: Date.now() };
532
532
  }
533
- await fs3.ensureDir(path3.dirname(HISTORY_FILE));
533
+ await fs3.ensureDir(path4.dirname(HISTORY_FILE));
534
534
  await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
535
535
  });
536
536
  };
@@ -539,13 +539,12 @@ var deleteChat = async (id) => {
539
539
  const history = await loadHistory();
540
540
  delete history[id];
541
541
  await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
542
- const tempFile = path3.join(AGENT_ROOT2, "secret/memory-temp.json");
543
- if (await fs3.pathExists(tempFile)) {
542
+ if (await fs3.pathExists(TEMP_MEM_FILE)) {
544
543
  try {
545
- const temp = await fs3.readJson(tempFile);
544
+ const temp = await fs3.readJson(TEMP_MEM_FILE);
546
545
  if (temp[id]) {
547
546
  delete temp[id];
548
- await fs3.writeJson(tempFile, temp, { spaces: 2 });
547
+ await fs3.writeJson(TEMP_MEM_FILE, temp, { spaces: 2 });
549
548
  }
550
549
  } catch (e) {
551
550
  }
@@ -582,15 +581,11 @@ var getTruncatedHistory = (history, exchangesToRemove = 4) => {
582
581
 
583
582
  // src/utils/usage.js
584
583
  import fs4 from "fs-extra";
585
- import path4 from "path";
586
- import { fileURLToPath as fileURLToPath3 } from "url";
587
- var __dirname3 = path4.dirname(fileURLToPath3(import.meta.url));
588
- var USAGE_PATH = path4.join(__dirname3, "../../secret/usage.json");
589
584
  var getDailyUsage = async () => {
590
585
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
591
586
  try {
592
- if (await fs4.exists(USAGE_PATH)) {
593
- const data = await fs4.readJson(USAGE_PATH);
587
+ if (await fs4.exists(USAGE_FILE)) {
588
+ const data = await fs4.readJson(USAGE_FILE);
594
589
  if (data.date === today) {
595
590
  return data.stats;
596
591
  }
@@ -599,19 +594,19 @@ var getDailyUsage = async () => {
599
594
  console.error("Failed to read usage:", err);
600
595
  }
601
596
  const defaultStats = { agent: 0, background: 0, search: 0 };
602
- await fs4.writeJson(USAGE_PATH, { date: today, stats: defaultStats }, { spaces: 2 });
597
+ await fs4.writeJson(USAGE_FILE, { date: today, stats: defaultStats }, { spaces: 2 });
603
598
  return defaultStats;
604
599
  };
605
600
  var incrementUsage = async (key) => {
606
601
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
607
- const data = await fs4.readJson(USAGE_PATH).catch(() => ({ date: today, stats: { agent: 0, background: 0, search: 0 } }));
602
+ const data = await fs4.readJson(USAGE_FILE).catch(() => ({ date: today, stats: { agent: 0, background: 0, search: 0 } }));
608
603
  if (data.date !== today) {
609
604
  data.date = today;
610
605
  data.stats = { agent: 0, background: 0, search: 0 };
611
606
  }
612
607
  if (data.stats[key] !== void 0) {
613
608
  data.stats[key]++;
614
- await fs4.writeJson(USAGE_PATH, data, { spaces: 2 });
609
+ await fs4.writeJson(USAGE_FILE, data, { spaces: 2 });
615
610
  }
616
611
  };
617
612
  var checkQuota = async (key, settings) => {
@@ -662,10 +657,6 @@ var parseArgs = (argsString) => {
662
657
  // src/tools/web_search.js
663
658
  import fs5 from "fs";
664
659
  import path5 from "path";
665
- import { fileURLToPath as fileURLToPath4 } from "url";
666
- var __filename3 = fileURLToPath4(import.meta.url);
667
- var __dirname4 = path5.dirname(__filename3);
668
- var AGENT_ROOT3 = path5.join(__dirname4, "../../");
669
660
  var web_search = async (argsString) => {
670
661
  const { query, limit = 10 } = parseArgs(argsString);
671
662
  if (!query) return 'ERROR: Missing "query" argument for web_search.';
@@ -693,7 +684,7 @@ Source: ${url}
693
684
  Snippet: ${snippet}`);
694
685
  count++;
695
686
  }
696
- const toolLogDir = path5.join(AGENT_ROOT3, "logs", "tools");
687
+ const toolLogDir = path5.join(LOGS_DIR, "tools");
697
688
  if (!fs5.existsSync(toolLogDir)) {
698
689
  fs5.mkdirSync(toolLogDir, { recursive: true });
699
690
  }
@@ -705,7 +696,7 @@ Results: ${results}
705
696
  `);
706
697
  if (results.length === 0) {
707
698
  if (html.includes("anomaly")) {
708
- const toolErrDir = path5.join(AGENT_ROOT3, "logs", "tools");
699
+ const toolErrDir = path5.join(LOGS_DIR, "tools");
709
700
  if (!fs5.existsSync(toolErrDir)) {
710
701
  fs5.mkdirSync(toolErrDir, { recursive: true });
711
702
  }
@@ -727,11 +718,7 @@ ${finalResults}`;
727
718
  // src/tools/web_scrape.js
728
719
  import fs6 from "fs";
729
720
  import path6 from "path";
730
- import { fileURLToPath as fileURLToPath5 } from "url";
731
721
  import * as cuimp2 from "cuimp";
732
- var __filename4 = fileURLToPath5(import.meta.url);
733
- var __dirname5 = path6.dirname(__filename4);
734
- var AGENT_ROOT4 = path6.join(__dirname5, "../../");
735
722
  var web_scrape = async (args) => {
736
723
  const urlMatch = args.match(/url\s*=\s*["'](.*)["']/);
737
724
  const url = urlMatch ? urlMatch[1] : args;
@@ -752,7 +739,7 @@ var web_scrape = async (args) => {
752
739
  html = html.replace(/<header\b[^<]*(?:(?!<\/header>)<[^<]*)*<\/header>/gi, "");
753
740
  let text = html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
754
741
  const finalContent = text.substring(0, 2e4);
755
- const toolLogDir = path6.join(AGENT_ROOT4, "logs", "tools");
742
+ const toolLogDir = path6.join(LOGS_DIR, "tools");
756
743
  if (!fs6.existsSync(toolLogDir)) {
757
744
  fs6.mkdirSync(toolLogDir, { recursive: true });
758
745
  }
@@ -771,14 +758,6 @@ ${finalContent}${text.length > 2e4 ? "\n\n[TRUNCATED AT 20K CHARS]" : ""}`;
771
758
  };
772
759
 
773
760
  // src/tools/memory.js
774
- import path7 from "path";
775
- import { fileURLToPath as fileURLToPath6 } from "url";
776
- var __filename5 = fileURLToPath6(import.meta.url);
777
- var __dirname6 = path7.dirname(__filename5);
778
- var AGENT_ROOT5 = path7.join(__dirname6, "../../");
779
- var SECRET_DIR2 = path7.join(AGENT_ROOT5, "secret");
780
- var MEMORIES_PATH = path7.join(SECRET_DIR2, "memories.json");
781
- var TEMP_MEM_PATH = path7.join(SECRET_DIR2, "memory-temp.json");
782
761
  var memory = async (rawArgs, context = {}) => {
783
762
  const parseArg = (key) => {
784
763
  const regex = new RegExp(`${key}\\s*=\\s*(["'])(.*?)\\1(?=\\s*[,)]|\\s+\\w+\\s*=|$)`, "s");
@@ -793,23 +772,23 @@ var memory = async (rawArgs, context = {}) => {
793
772
  const chatId = parseArg("chat-id") || context.chatId || context.sessionId || "default-session";
794
773
  if (action === "temp") {
795
774
  if (!content) return "ERROR: Missing 'content' for temp memory.";
796
- const tempStorage = readEncryptedJson(TEMP_MEM_PATH, {});
775
+ const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
797
776
  if (!tempStorage[chatId]) tempStorage[chatId] = [];
798
- const MAX_CHARS = 4e3 * 4;
777
+ const MAX_CHARS = 3e3 * 4;
799
778
  let currentTotalLength = tempStorage[chatId].reduce((acc, m) => acc + m.length, 0);
800
779
  while (tempStorage[chatId].length > 0 && currentTotalLength + content.length > MAX_CHARS) {
801
780
  const removed = tempStorage[chatId].shift();
802
781
  currentTotalLength -= removed.length;
803
782
  }
804
783
  tempStorage[chatId].push(content);
805
- writeEncryptedJson(TEMP_MEM_PATH, tempStorage);
784
+ writeEncryptedJson(TEMP_MEM_FILE, tempStorage);
806
785
  return `SUCCESS: Temporary context saved for session [${chatId}]. (Size: ${currentTotalLength + content.length} chars)`;
807
786
  }
808
787
  if (action === "user") {
809
- const memories = readEncryptedJson(MEMORIES_PATH, []);
788
+ const memories = readEncryptedJson(MEMORIES_FILE, []);
810
789
  if (method === "add") {
811
790
  if (!content) return "ERROR: Missing 'content' for memory addition.";
812
- const MAX_CHARS = 3e3 * 4;
791
+ const MAX_CHARS = 2e3 * 4;
813
792
  let currentTotalLength = memories.reduce((acc, m) => acc + (m.memory?.length || 0), 0);
814
793
  while (memories.length > 0 && currentTotalLength + content.length > MAX_CHARS) {
815
794
  const removed = memories.shift();
@@ -817,7 +796,7 @@ var memory = async (rawArgs, context = {}) => {
817
796
  }
818
797
  const newMemory = { id: `mem-${Date.now().toString(36)}`, memory: content };
819
798
  memories.push(newMemory);
820
- writeEncryptedJson(MEMORIES_PATH, memories);
799
+ writeEncryptedJson(MEMORIES_FILE, memories);
821
800
  return `SUCCESS: Memory added with ID [${newMemory.id}]. (Vault Size: ${currentTotalLength + content.length} chars)`;
822
801
  }
823
802
  if (method === "update") {
@@ -827,7 +806,7 @@ var memory = async (rawArgs, context = {}) => {
827
806
  const index = memories.findIndex((m) => m.id === memId);
828
807
  if (index === -1) return `ERROR: Memory ID [${memId}] not found.`;
829
808
  memories[index].memory = newText;
830
- writeEncryptedJson(MEMORIES_PATH, memories);
809
+ writeEncryptedJson(MEMORIES_FILE, memories);
831
810
  return `SUCCESS: Memory [${memId}] updated.`;
832
811
  }
833
812
  if (method === "delete") {
@@ -836,7 +815,7 @@ var memory = async (rawArgs, context = {}) => {
836
815
  const initialLen = memories.length;
837
816
  const updatedMemories = memories.filter((m) => m.id !== memId);
838
817
  if (updatedMemories.length === initialLen) return `ERROR: Memory ID [${memId}] not found.`;
839
- writeEncryptedJson(MEMORIES_PATH, updatedMemories);
818
+ writeEncryptedJson(MEMORIES_FILE, updatedMemories);
840
819
  return `SUCCESS: Memory [${memId}] deleted.`;
841
820
  }
842
821
  return `ERROR: Invalid method [${method}] for user memory. Use 'add', 'update', or 'delete'.`;
@@ -900,10 +879,10 @@ var summary = async (rawArgs, context = {}) => {
900
879
 
901
880
  // src/tools/list_files.js
902
881
  import fs7 from "fs";
903
- import path8 from "path";
882
+ import path7 from "path";
904
883
  var list_files = async (args) => {
905
884
  const { path: targetPath = "." } = parseArgs(args);
906
- const absolutePath = path8.resolve(process.cwd(), targetPath);
885
+ const absolutePath = path7.resolve(process.cwd(), targetPath);
907
886
  try {
908
887
  if (!fs7.existsSync(absolutePath)) {
909
888
  return `ERROR: Path [${targetPath}] does not exist.`;
@@ -920,7 +899,7 @@ var list_files = async (args) => {
920
899
  const maxDisplay = 100;
921
900
  const displayFiles = files2.slice(0, maxDisplay);
922
901
  const list = displayFiles.map((file) => {
923
- const fPath = path8.join(absolutePath, file);
902
+ const fPath = path7.join(absolutePath, file);
924
903
  let indicator = "\u{1F4C4}";
925
904
  let metaPart = "";
926
905
  try {
@@ -955,11 +934,11 @@ ${list}${footer}`;
955
934
 
956
935
  // src/tools/view_file.js
957
936
  import fs8 from "fs";
958
- import path9 from "path";
937
+ import path8 from "path";
959
938
  var view_file = async (args) => {
960
939
  const { path: targetPath, start_line = 1, end_line = 500 } = parseArgs(args);
961
940
  if (!targetPath) return 'ERROR: Missing "path" argument for view_file.';
962
- const absolutePath = path9.resolve(process.cwd(), targetPath);
941
+ const absolutePath = path8.resolve(process.cwd(), targetPath);
963
942
  try {
964
943
  if (!fs8.existsSync(absolutePath)) {
965
944
  return `ERROR: File [${targetPath}] does not exist.`;
@@ -986,14 +965,14 @@ ${code}`;
986
965
 
987
966
  // src/tools/write_file.js
988
967
  import fs9 from "fs";
989
- import path10 from "path";
968
+ import path9 from "path";
990
969
  var write_file = async (args) => {
991
970
  let { path: targetPath, content } = parseArgs(args);
992
971
  if (!targetPath) return 'ERROR: Missing "path" argument for write_file.';
993
972
  if (content === void 0) return 'ERROR: Missing "content" argument for write_file.';
994
973
  content = content.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
995
- const absolutePath = path10.resolve(process.cwd(), targetPath);
996
- const parentDir = path10.dirname(absolutePath);
974
+ const absolutePath = path9.resolve(process.cwd(), targetPath);
975
+ const parentDir = path9.dirname(absolutePath);
997
976
  try {
998
977
  if (!fs9.existsSync(parentDir)) {
999
978
  fs9.mkdirSync(parentDir, { recursive: true });
@@ -1034,7 +1013,7 @@ ${snippet}`;
1034
1013
 
1035
1014
  // src/tools/update_file.js
1036
1015
  import fs10 from "fs";
1037
- import path11 from "path";
1016
+ import path10 from "path";
1038
1017
  var update_file = async (args) => {
1039
1018
  let { path: targetPath, content_to_replace, content_to_add } = parseArgs(args);
1040
1019
  if (!targetPath) return 'ERROR: Missing "path" argument for update_file.';
@@ -1043,7 +1022,7 @@ var update_file = async (args) => {
1043
1022
  const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
1044
1023
  content_to_replace = strip(content_to_replace);
1045
1024
  content_to_add = strip(content_to_add);
1046
- const absolutePath = path11.resolve(process.cwd(), targetPath);
1025
+ const absolutePath = path10.resolve(process.cwd(), targetPath);
1047
1026
  try {
1048
1027
  if (!fs10.existsSync(absolutePath)) {
1049
1028
  return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
@@ -1136,10 +1115,10 @@ ${finalOutput}`);
1136
1115
 
1137
1116
  // src/tools/read_folder.js
1138
1117
  import fs11 from "fs";
1139
- import path12 from "path";
1118
+ import path11 from "path";
1140
1119
  var read_folder = async (args) => {
1141
1120
  const { path: targetPath = "." } = parseArgs(args);
1142
- const absolutePath = path12.resolve(process.cwd(), targetPath);
1121
+ const absolutePath = path11.resolve(process.cwd(), targetPath);
1143
1122
  try {
1144
1123
  if (!fs11.existsSync(absolutePath)) {
1145
1124
  return `ERROR: Path [${targetPath}] does not exist.`;
@@ -1153,7 +1132,7 @@ var read_folder = async (args) => {
1153
1132
  const displayItems = files.slice(0, maxDisplay);
1154
1133
  const folderData = [];
1155
1134
  for (const file of displayItems) {
1156
- const fPath = path12.join(absolutePath, file);
1135
+ const fPath = path11.join(absolutePath, file);
1157
1136
  let indicator = "\u{1F4C4}";
1158
1137
  let info = { name: file, type: "unknown", size: "N/A", mtime: "N/A" };
1159
1138
  try {
@@ -1223,15 +1202,9 @@ var dispatchTool = async (toolName, args, context = {}) => {
1223
1202
  };
1224
1203
 
1225
1204
  // src/utils/ai.js
1226
- import { fileURLToPath as fileURLToPath7 } from "url";
1227
- import path13 from "path";
1205
+ import path12 from "path";
1228
1206
  import fs12 from "fs";
1229
- var __filename6 = fileURLToPath7(import.meta.url);
1230
- var __dirname7 = path13.dirname(__filename6);
1231
- var AGENT_ROOT6 = path13.join(__dirname7, "../../");
1232
1207
  var client = null;
1233
- var TEMP_MEM_PATH2 = path13.join(AGENT_ROOT6, "secret", "memory-temp.json");
1234
- var PERSISTENT_MEM_PATH = path13.join(AGENT_ROOT6, "secret", "memories.json");
1235
1208
  var TERMINATION_SIGNAL = false;
1236
1209
  var signalTermination = () => {
1237
1210
  TERMINATION_SIGNAL = true;
@@ -1285,12 +1258,12 @@ var getAIStream = async function* (modelName, history, settings, steeringCallbac
1285
1258
  const needTitle = isFirstPrompt || hasTitleSignal;
1286
1259
  const agentText = originalText.replace(/\[TITLE-UPDATE\]/g, "").trim();
1287
1260
  let modifiedHistory = [...history.slice(0, -1)];
1288
- if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 128e3) {
1261
+ if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 196e3) {
1289
1262
  modifiedHistory = getTruncatedHistory(modifiedHistory, 4);
1290
1263
  }
1291
- const tempStorage = readEncryptedJson(TEMP_MEM_PATH2, {});
1264
+ const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
1292
1265
  const otherMemories = Object.entries(tempStorage).filter(([id]) => id !== chatId).flatMap(([_, mems]) => mems).map((mem) => `- ${mem}`).join("\n");
1293
- const persistentStorage = readEncryptedJson(PERSISTENT_MEM_PATH, []);
1266
+ const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
1294
1267
  const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
1295
1268
  const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
1296
1269
  const systemInstruction = getSystemInstruction(profile, thinkingLevel, mode, systemSettings, otherMemories, mainUserMemories, isMemoryEnabled);
@@ -1344,11 +1317,11 @@ USER_PROMPT: ${agentText}`.trim();
1344
1317
  } catch (err) {
1345
1318
  const errMsg = err.message || String(err);
1346
1319
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1347
- const agentErrDir = path13.join(AGENT_ROOT6, "logs", "agent");
1320
+ const agentErrDir = path12.join(LOGS_DIR, "agent");
1348
1321
  if (!fs12.existsSync(agentErrDir)) {
1349
1322
  fs12.mkdirSync(agentErrDir, { recursive: true });
1350
1323
  }
1351
- fs12.appendFileSync(path13.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
1324
+ fs12.appendFileSync(path12.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
1352
1325
  `);
1353
1326
  const isRetryable = errMsg.includes("429") || errMsg.includes("503") || errMsg.includes("overloaded") || errMsg.includes("deadline");
1354
1327
  if (isRetryable && retryCount < MAX_RETRIES) {
@@ -1394,7 +1367,17 @@ USER_PROMPT: ${agentText}`.trim();
1394
1367
  const url = parseArgs(toolCall.args).url || "...";
1395
1368
  label = `\u{1F4D6} READING SITE: ${url}`.toUpperCase();
1396
1369
  } else if (toolCall.toolName === "view_file") {
1397
- label = `\u{1F4C4} READING FILE: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
1370
+ const { path: targetPath2, start_line = 1, end_line = 500 } = parseArgs(toolCall.args);
1371
+ let totalLines = "...";
1372
+ try {
1373
+ const absPath = path12.resolve(process.cwd(), targetPath2);
1374
+ if (fs12.existsSync(absPath)) {
1375
+ const content = fs12.readFileSync(absPath, "utf8");
1376
+ totalLines = content.split("\n").length;
1377
+ }
1378
+ } catch (e) {
1379
+ }
1380
+ label = `\u{1F4C4} READING FILE: ${targetPath2}. LINES ${start_line} - ${end_line} FROM ${totalLines}`.toUpperCase();
1398
1381
  } else if (toolCall.toolName === "list_files" || toolCall.toolName === "read_folder") {
1399
1382
  const action = toolCall.toolName === "list_files" ? "LISTING" : "DISCOVERING";
1400
1383
  label = `\u{1F4C2} ${action} DIRECTORY: ${parseArgs(toolCall.args).path || "."}`.toUpperCase();
@@ -1435,7 +1418,7 @@ ${boxBottom}
1435
1418
  /\/usr\//
1436
1419
  // Sensitive Linux paths
1437
1420
  ];
1438
- const currentDrive = path13.resolve(process.cwd()).substring(0, 3).toLowerCase();
1421
+ const currentDrive = path12.resolve(process.cwd()).substring(0, 3).toLowerCase();
1439
1422
  const isViolating = riskyPatterns.some((pattern) => {
1440
1423
  if (pattern.source === "[a-zA-Z]:[\\\\\\/]") {
1441
1424
  const driveMatch = command.match(/[a-zA-Z]:[\\\/]/i);
@@ -1457,8 +1440,8 @@ ${boxBottom}
1457
1440
  const targetPath = parsedArgs.path || parsedArgs.targetPath || null;
1458
1441
  if (targetPath) {
1459
1442
  const isExternalOff = settings.systemSettings && settings.systemSettings.allowExternalAccess === false;
1460
- const absoluteTarget = path13.resolve(targetPath);
1461
- const absoluteCwd = path13.resolve(process.cwd());
1443
+ const absoluteTarget = path12.resolve(targetPath);
1444
+ const absoluteCwd = path12.resolve(process.cwd());
1462
1445
  if (isExternalOff && !absoluteTarget.startsWith(absoluteCwd)) {
1463
1446
  const denyMsg = `Access Denied. You are not allowed to access files outside the current workspace. To enable this, ask the user to turn on "External Workspace Access" in /settings.`;
1464
1447
  toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
@@ -1497,11 +1480,11 @@ ${boxBottom}
1497
1480
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1498
1481
  const isErr = result.startsWith("ERROR:");
1499
1482
  const logStatus = isErr ? result.trim() : "SUCCESS";
1500
- const toolHistDir = path13.join(AGENT_ROOT6, "logs", "tools");
1483
+ const toolHistDir = path12.join(LOGS_DIR, "tools");
1501
1484
  if (!fs12.existsSync(toolHistDir)) {
1502
1485
  fs12.mkdirSync(toolHistDir, { recursive: true });
1503
1486
  }
1504
- fs12.appendFileSync(path13.join(toolHistDir, "history.log"), `HISTORY [${timestamp}]: ${toolCall.toolName} [${logStatus}]
1487
+ fs12.appendFileSync(path12.join(toolHistDir, "history.log"), `HISTORY [${timestamp}]: ${toolCall.toolName} [${logStatus}]
1505
1488
  `);
1506
1489
  } catch (logErr) {
1507
1490
  }
@@ -1555,11 +1538,11 @@ ${boxBottom}
1555
1538
  if (parts && parts[1]?.text) {
1556
1539
  finalSynthesis = parts[1].text;
1557
1540
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1558
- const janitorLogDir = path13.join(AGENT_ROOT6, "logs", "janitor");
1541
+ const janitorLogDir = path12.join(LOGS_DIR, "janitor");
1559
1542
  if (!fs12.existsSync(janitorLogDir)) {
1560
1543
  fs12.mkdirSync(janitorLogDir, { recursive: true });
1561
1544
  }
1562
- fs12.appendFileSync(path13.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: ${finalSynthesis}
1545
+ fs12.appendFileSync(path12.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: ${finalSynthesis}
1563
1546
  `);
1564
1547
  } else if (parts && parts[0]?.text) finalSynthesis = parts[0].text;
1565
1548
  else if (janitorResult.response && janitorResult.response.text) finalSynthesis = janitorResult.response.text();
@@ -1571,8 +1554,8 @@ ${boxBottom}
1571
1554
  const toolContext = { chatId, sessionId: chatId, history };
1572
1555
  const result = await dispatchTool(janitorToolCall.toolName, janitorToolCall.args, toolContext);
1573
1556
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1574
- const janitorLogDir = path13.join(AGENT_ROOT6, "logs", "janitor");
1575
- fs12.appendFileSync(path13.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
1557
+ const janitorLogDir = path12.join(LOGS_DIR, "janitor");
1558
+ fs12.appendFileSync(path12.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
1576
1559
  `);
1577
1560
  if (janitorToolCall.toolName === "memory" && !janitorToolCall.args.includes("action='temp'")) {
1578
1561
  yield { type: "memory_updated" };
@@ -1580,11 +1563,11 @@ ${boxBottom}
1580
1563
  }
1581
1564
  } catch (janitorErr) {
1582
1565
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1583
- const janitorErrDir = path13.join(AGENT_ROOT6, "logs", "janitor");
1566
+ const janitorErrDir = path12.join(LOGS_DIR, "janitor");
1584
1567
  if (!fs12.existsSync(janitorErrDir)) {
1585
1568
  fs12.mkdirSync(janitorErrDir, { recursive: true });
1586
1569
  }
1587
- fs12.appendFileSync(path13.join(janitorErrDir, "error.log"), `ERROR [${date}]: ${janitorErr.message}
1570
+ fs12.appendFileSync(path12.join(janitorErrDir, "error.log"), `ERROR [${date}]: ${janitorErr.message}
1588
1571
  `);
1589
1572
  console.error("Janitor Background Tasks Failed:", janitorErr.message);
1590
1573
  }
@@ -1612,10 +1595,6 @@ ${timestamp}`;
1612
1595
 
1613
1596
  // src/utils/settings.js
1614
1597
  import fs13 from "fs-extra";
1615
- import path14 from "path";
1616
- import { fileURLToPath as fileURLToPath8 } from "url";
1617
- var __dirname8 = path14.dirname(fileURLToPath8(import.meta.url));
1618
- var SETTINGS_PATH = path14.join(__dirname8, "../../settings.json");
1619
1598
  var DEFAULT_SETTINGS = {
1620
1599
  mode: "Flux",
1621
1600
  thinkingLevel: "Medium",
@@ -1644,8 +1623,8 @@ var DEFAULT_SETTINGS = {
1644
1623
  };
1645
1624
  var loadSettings = async () => {
1646
1625
  try {
1647
- if (await fs13.exists(SETTINGS_PATH)) {
1648
- const saved = await fs13.readJson(SETTINGS_PATH);
1626
+ if (await fs13.exists(SETTINGS_FILE)) {
1627
+ const saved = await fs13.readJson(SETTINGS_FILE);
1649
1628
  return {
1650
1629
  ...DEFAULT_SETTINGS,
1651
1630
  ...saved,
@@ -1663,7 +1642,7 @@ var saveSettings = async (settings) => {
1663
1642
  try {
1664
1643
  const current = await loadSettings();
1665
1644
  const updated = { ...current, ...settings };
1666
- await fs13.writeJson(SETTINGS_PATH, updated, { spaces: 2 });
1645
+ await fs13.writeJson(SETTINGS_FILE, updated, { spaces: 2 });
1667
1646
  return true;
1668
1647
  } catch (err) {
1669
1648
  console.error("Failed to save settings:", err);
@@ -1712,13 +1691,13 @@ function ResumeModal({ onSelect, onDelete, onClose }) {
1712
1691
  // src/components/MemoryModal.jsx
1713
1692
  import React7, { useState as useState3, useEffect as useEffect2 } from "react";
1714
1693
  import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
1715
- import path15 from "path";
1716
- var MEMORIES_PATH2 = path15.join(process.cwd(), "secret", "memories.json");
1694
+ import path13 from "path";
1695
+ var MEMORIES_PATH = path13.join(process.cwd(), "secret", "memories.json");
1717
1696
  function MemoryModal({ onClose }) {
1718
1697
  const [memories, setMemories] = useState3([]);
1719
1698
  const [selectedIndex, setSelectedIndex] = useState3(0);
1720
1699
  const loadMemories = () => {
1721
- const data = readEncryptedJson(MEMORIES_PATH2, []);
1700
+ const data = readEncryptedJson(MEMORIES_PATH, []);
1722
1701
  setMemories(data);
1723
1702
  };
1724
1703
  useEffect2(() => {
@@ -1731,7 +1710,7 @@ function MemoryModal({ onClose }) {
1731
1710
  if (input === "x" && memories.length > 0) {
1732
1711
  const idToDelete = memories[selectedIndex].id;
1733
1712
  const updated = memories.filter((m) => m.id !== idToDelete);
1734
- writeEncryptedJson(MEMORIES_PATH2, updated);
1713
+ writeEncryptedJson(MEMORIES_PATH, updated);
1735
1714
  setMemories(updated);
1736
1715
  if (selectedIndex >= updated.length && updated.length > 0) {
1737
1716
  setSelectedIndex(updated.length - 1);
@@ -2086,18 +2065,19 @@ ${list || "No saved chats found."}` }];
2086
2065
  }
2087
2066
  case "/reset": {
2088
2067
  const runReset = async () => {
2089
- const AGENT_ROOT7 = path16.join(path16.dirname(fileURLToPath9(import.meta.url)), "../");
2090
- const logsDir = path16.join(AGENT_ROOT7, "logs");
2091
- const secretDir = path16.join(AGENT_ROOT7, "secret");
2092
- const settingsFile = path16.join(AGENT_ROOT7, "settings.json");
2093
2068
  try {
2094
2069
  setMessages((prev) => {
2095
2070
  setCompletedIndex(prev.length + 1);
2096
2071
  return [...prev, { id: Date.now(), role: "system", text: "\u2622\uFE0F [NUCLEAR] Initiating reset..." }];
2097
2072
  });
2098
- if (fs14.existsSync(logsDir)) fs14.removeSync(logsDir);
2099
- if (fs14.existsSync(secretDir)) fs14.removeSync(secretDir);
2100
- if (fs14.existsSync(settingsFile)) fs14.removeSync(settingsFile);
2073
+ if (fs14.existsSync(LOGS_DIR)) fs14.removeSync(LOGS_DIR);
2074
+ if (fs14.existsSync(SECRET_DIR)) fs14.removeSync(SECRET_DIR);
2075
+ if (fs14.existsSync(SETTINGS_FILE)) fs14.removeSync(SETTINGS_FILE);
2076
+ try {
2077
+ const items = fs14.readdirSync(FLUXFLOW_DIR);
2078
+ if (items.length === 0) fs14.removeSync(FLUXFLOW_DIR);
2079
+ } catch (e) {
2080
+ }
2101
2081
  setTimeout(() => {
2102
2082
  setActiveView("exit");
2103
2083
  setTimeout(() => process.exit(0), 500);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",
@@ -23,15 +23,18 @@
23
23
  "dist",
24
24
  "README.md",
25
25
  "package.json",
26
- "ARCHITECTURE.md"
26
+ "ARCHITECTURE.md",
27
+ "TOOLS.md",
28
+ "UI_FEATURES.md"
27
29
  ],
28
30
  "type": "module",
29
31
  "bin": {
30
- "fluxflow-cli": "dist/index.js"
32
+ "fluxflow-cli": "dist/fluxflow.js",
33
+ "fluxflow": "dist/fluxflow.js"
31
34
  },
32
35
  "scripts": {
33
36
  "start": "tsx ./src/cli.jsx",
34
- "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/index.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:duck-duck-scrape --external:nanoid --external:cuimp"
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:duck-duck-scrape --external:nanoid --external:cuimp"
35
38
  },
36
39
  "dependencies": {
37
40
  "@google/genai": "^1.50.1",