fluxflow-cli 1.7.19 → 1.7.20

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.
Files changed (2) hide show
  1. package/dist/fluxflow.js +61 -68
  2. package/package.json +1 -1
package/dist/fluxflow.js CHANGED
@@ -75,47 +75,11 @@ var init_TerminalBox = __esm({
75
75
  // src/components/ChatLayout.jsx
76
76
  import React2, { useState, useEffect, useRef } from "react";
77
77
  import { Box as Box2, Text as Text2 } from "ink";
78
- var TypewriterText, cleanSignals, formatThinkText, parseMathSymbols, InlineMarkdown, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, MessageItem, ChatLayout, ChatLayout_default;
78
+ var cleanSignals, formatThinkText, parseMathSymbols, InlineMarkdown, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, MessageItem, ChatLayout, ChatLayout_default;
79
79
  var init_ChatLayout = __esm({
80
80
  "src/components/ChatLayout.jsx"() {
81
81
  init_TerminalBox();
82
82
  init_text();
83
- TypewriterText = ({ text, isStreaming, onComplete, columns = 80, color = "white", speed = 50, render }) => {
84
- const [displayedText, setDisplayedText] = useState("");
85
- const fullTextRef = useRef(text);
86
- const displayedTextRef = useRef("");
87
- useEffect(() => {
88
- fullTextRef.current = text;
89
- }, [text]);
90
- useEffect(() => {
91
- const timer = setInterval(() => {
92
- const currentFull = fullTextRef.current;
93
- const currentDisp = displayedTextRef.current;
94
- if (currentDisp.length < currentFull.length) {
95
- const remaining = currentFull.slice(currentDisp.length);
96
- const match = remaining.match(/^(\S+\s*|\s+)/);
97
- if (match) {
98
- const chunk = match[0];
99
- const gap = currentFull.length - currentDisp.length;
100
- let revealedChunk = chunk;
101
- if (gap > 100) {
102
- const extraMatch = remaining.slice(chunk.length).match(/^(\S+\s*|\s+){0,2}/);
103
- if (extraMatch) revealedChunk += extraMatch[0];
104
- }
105
- const nextText = currentDisp + revealedChunk;
106
- setDisplayedText(nextText);
107
- displayedTextRef.current = nextText;
108
- }
109
- } else if (!isStreaming) {
110
- if (onComplete) onComplete();
111
- clearInterval(timer);
112
- }
113
- }, speed);
114
- return () => clearInterval(timer);
115
- }, [isStreaming, speed]);
116
- if (render) return render(displayedText);
117
- return /* @__PURE__ */ React2.createElement(CodeRenderer, { text: displayedText, columns });
118
- };
119
83
  cleanSignals = (text) => {
120
84
  if (!text) return text;
121
85
  let result = text;
@@ -455,24 +419,7 @@ var init_ChatLayout = __esm({
455
419
  finalContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\n/g, "\n").replace(/\\$/, ""),
456
420
  columns - 6
457
421
  ).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))))
458
- ) : 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(
459
- TypewriterText,
460
- {
461
- text: finalContent,
462
- isStreaming: msg.isStreaming,
463
- onComplete: () => setAnimationDone(true),
464
- speed: 25,
465
- render: (t) => formatThinkText(t, columns)
466
- }
467
- ) : formatThinkText(finalContent, columns))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1, width: "100%" }, !animationDone ? /* @__PURE__ */ React2.createElement(
468
- TypewriterText,
469
- {
470
- text: finalContent,
471
- isStreaming: msg.isStreaming,
472
- onComplete: () => setAnimationDone(true),
473
- columns
474
- }
475
- ) : /* @__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]"))));
422
+ ) : 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]"))));
476
423
  });
477
424
  ChatLayout = React2.memo(({ messages, showFullThinking, columns = 80 }) => {
478
425
  return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, messages.map((msg, idx) => /* @__PURE__ */ React2.createElement(
@@ -769,7 +716,7 @@ ${mode === "Flux" ? `
769
716
  2. List Files: tool:functions.list_files(path="relative/path"). Lists content of a directory.
770
717
  3. Read Folder: tool:functions.read_folder(path="relative/path"). Detailed stats of a directory. Prefer this one over list_files.
771
718
  4. Write File: tool:functions.write_file(path="path", content="content"). Creates/Overwrites. NO CODE BLOCKS. RETURNS: Disk verification + original content (if overwritten) for 100% reversibility. Escape your double quotes '"' using backslash.
772
- 5. Update File: tool:functions.update_file(path="relative/path", content_to_replace="old", content_to_add="new"). Surgical patching. RETURNS: High-fidelity visual diff and old code block. You MUST verify that the change specifically matches your intent using the returned diff. PREFFER UPDATE FILE OVER WRITE FILE if file already exists for better reversal tracking (if a file has 500+ lines, try to stick with update_file over full-rewrite). DONT WRAP UPDATE FILE CALL CONTENT IN MARKDOWN CODE BLOCKS.
719
+ 5. Update File: tool:functions.update_file(path="relative/path", content_to_replace="old", content_to_add="new"). Surgical patching. RETURNS: High-fidelity visual diff and old code block. You MUST verify that the change specifically matches your intent using the returned diff. PREFFER UPDATE FILE OVER WRITE FILE if file already exists for better reversal tracking (if a file has 500+ lines, try to stick with update_file over full-rewrite). DONT WRAP UPDATE FILE CALL CONTENT IN MARKDOWN CODE BLOCKS. KEEP YOUR PATCH INDENTATION AT BASE LEVEL WITH INTERNAL NESTED INDENTATION IN YOUR PROVIDED CODE INTACT, THE SYSTEM WILL HANDLE FILE LEVEL INDENTATION.
773
720
  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, USE full 100vh & 100vw for page area. ENSURE THE CONTENT IS NEVER BROKEN IN BETWEEN PAGES, USE PAGE BREAKS PROACTIVELY FOR A A4 PAGE LAYOUT. Keep generous margins for better redability.
774
721
  7. Write DOCX: tool:functions.write_docx(path="path", content="<html content>"). Generates a professional Word document (.docx) from HTML. You can make multiple pages. Default Page dimentions will be A4, use proper margins and page break strategy.
775
722
  8. Write PPTX: tool:functions.write_pptx(path="path", content="<h1 style='color: #0088CC;'>Title</h1><ul style='font-size: 14pt;'><li>Point A</li></ul>
@@ -781,7 +728,7 @@ ${mode === "Flux" ? `
781
728
 
782
729
  AFTER GETTING THE TOOL RESULT, YOU MUST VERIFY THAT ITS A SUCCESS, IF IT GIVES A ERROR, TELL THE USER AND TRY TO FIX IF YOU CAN. DO NOT HALLUCINATE SUCCESS IF TOOL RETURNS ERROR.
783
730
  NEVER GUESS A CODE, IF UNSURE READ THE FILE FIRST BEFORE EDITING IT.
784
- **CRITICAL POLICY: WHEN WRITING/UPDATING FILES, ALWAYS USE ACTUAL NEW LINE CONTROL CHARACTER (LF) FOR LINE BREAKS.**`.trim() : `
731
+ **CRITICAL POLICY: WHEN WRITING/UPDATING FILES, ALWAYS USE ACTUAL NEW LINE CONTROL CHARACTER (LF) FOR LINE BREAKS. WHEN YOU WANT TO WRITE \\n IN THE FILE PROPERLY ESPACE IT.**`.trim() : `
785
732
  - 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()}
786
733
  -----------------
787
734
 
@@ -912,6 +859,7 @@ Every ${isMemoryEnabled ? "Prompt, Responses & Memories" : "Prompt & Responses"}
912
859
  - **CRITICAL**: NEVER USE LaTeX IN RESPONSES.
913
860
  - Keep Poems & Literature in Code Block.
914
861
  - Use emojis & Kaomojis. Prefer Kaomojis more.
862
+ - Keep your in-chat responses shorter and concise.
915
863
  -- END FORMATTING RULES --
916
864
 
917
865
  -- START REPONSE FINISH PROTOCOL --
@@ -947,6 +895,7 @@ YOU ARE A SILENT BACKGROUND SYSTEM PROCESS. YOU HAVE NO MOUTH. YOUR ONLY OUTPUT
947
895
  3. NON-TOOL TEXT WILL BREAK THE SYSTEM.
948
896
  4. DO NOT REPEAT AGENT RAWS AND TOOL RESULTS IN YOUR RESPONSE.
949
897
  5. IF YOU GET ONLY USER QUERY AND NO AGENT RAWS, THEN JUST USE TEMP MEMORY TO LOG THE SUMMARY OF USER QUERY.
898
+ 6. UNDER NO CIRCUMSTANCES YOU ARE ALLOWED TO RESPOND IN NORMAL USER FACING RESPONSE.
950
899
 
951
900
  YOUR JOB: Analyze the 'User prompt' and 'Agent Raws' to extract facts for long-term memory or handle system tasks.
952
901
  ${isMemoryEnabled ? `If user tell something that is important (like, hobbies, preferences, facts about user, hates, likes, etc) to know user better over time, use long term memory tools.` : ""}
@@ -1859,18 +1808,61 @@ var init_update_file = __esm({
1859
1808
  diskContent = normalizedDisk;
1860
1809
  }
1861
1810
  const currentContent = diskContent;
1862
- if (!currentContent.includes(content_to_replace)) {
1811
+ const adjustIndentation = (newText, originalMatch) => {
1812
+ if (!newText || !originalMatch) return newText;
1813
+ const getMinIndent = (text) => {
1814
+ const lines = text.split("\n").filter((l) => l.trim() !== "");
1815
+ if (lines.length === 0) return "";
1816
+ let min = lines[0].match(/^\s*/)[0];
1817
+ for (const line of lines) {
1818
+ const indent = line.match(/^\s*/)[0];
1819
+ if (indent.length < min.length) min = indent;
1820
+ }
1821
+ return min;
1822
+ };
1823
+ const originalMinIndent = getMinIndent(originalMatch);
1824
+ const newMinIndent = getMinIndent(newText);
1825
+ const newLines2 = newText.split("\n");
1826
+ return newLines2.map((line) => {
1827
+ if (line.trim() === "") return "";
1828
+ if (line.startsWith(newMinIndent)) {
1829
+ return originalMinIndent + line.substring(newMinIndent.length);
1830
+ }
1831
+ return originalMinIndent + line.trimStart();
1832
+ }).join("\n");
1833
+ };
1834
+ let matchRegex = null;
1835
+ let instances = 0;
1836
+ let startPos = -1;
1837
+ if (currentContent.includes(content_to_replace)) {
1838
+ instances = currentContent.split(content_to_replace).length - 1;
1839
+ startPos = currentContent.indexOf(content_to_replace);
1840
+ } else {
1841
+ const escaped = content_to_replace.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1842
+ const fuzzyPattern = escaped.trim().replace(/\s+/g, "\\s+");
1843
+ try {
1844
+ const fuzzyRegex = new RegExp(fuzzyPattern, "g");
1845
+ const matches = [...currentContent.matchAll(fuzzyRegex)];
1846
+ if (matches.length > 0) {
1847
+ matchRegex = fuzzyRegex;
1848
+ instances = matches.length;
1849
+ startPos = matches[0].index;
1850
+ content_to_replace = matches[0][0];
1851
+ }
1852
+ } catch (e) {
1853
+ }
1854
+ }
1855
+ if (instances === 0) {
1863
1856
  const diskLen = currentContent.length;
1864
1857
  const matchLen = content_to_replace.length;
1865
- return `ERROR: Could not find exact match for the specified "content_to_replace" in [${targetPath}].
1858
+ return `ERROR: Could not find match (even fuzzy) for the specified "content_to_replace" in [${targetPath}].
1866
1859
  - Disk Content Length (Normalized): ${diskLen}
1867
1860
  - Match String Length (Normalized): ${matchLen}
1868
1861
  - Check indentation/whitespace. Try re-reading the file for latest changes.`;
1869
1862
  }
1870
- const startPos = currentContent.indexOf(content_to_replace);
1871
1863
  const startLine = currentContent.substring(0, startPos).split(/\r?\n/).length;
1872
- const instances = currentContent.split(content_to_replace).length - 1;
1873
- const newFileContent = currentContent.split(content_to_replace).join(content_to_add);
1864
+ const newFileContent = matchRegex ? currentContent.replace(matchRegex, (match) => adjustIndentation(content_to_add, match)) : currentContent.split(content_to_replace).join(adjustIndentation(content_to_add, content_to_replace));
1865
+ content_to_add = adjustIndentation(content_to_add, content_to_replace);
1874
1866
  fs11.writeFileSync(absolutePath, newFileContent, "utf8");
1875
1867
  const allOriginalLines = currentContent.split(/\r?\n/);
1876
1868
  const oldLines = content_to_replace.split(/\r?\n/);
@@ -2603,7 +2595,7 @@ USER_PROMPT: ${agentText}`.trim();
2603
2595
  const headingSections = thinkContent.split(/^\s*\*\*.*?\*\*\s*$/gm);
2604
2596
  const isOverVerbose = headingSections.some((section) => {
2605
2597
  const wordCount = section.trim().split(/\s+/).filter((w) => w.length > 0).length;
2606
- return wordCount > 450;
2598
+ return wordCount > 500;
2607
2599
  });
2608
2600
  if (headingsCount > 25 || isOverVerbose) {
2609
2601
  const reason = headingsCount > 25 ? "Loop Detected" : "Noise Detected";
@@ -2811,6 +2803,7 @@ ${boxBottom}
2811
2803
  model: janitorModel || "gemma-4-26b-a4b-it",
2812
2804
  contents: janitorContents,
2813
2805
  config: {
2806
+ maxOutputTokens: 512,
2814
2807
  thinkingConfig: {
2815
2808
  includeThoughts: false,
2816
2809
  thinkingLevel: ThinkingLevel.MINIMAL
@@ -2873,7 +2866,7 @@ ${timestamp}`;
2873
2866
  if (toolResults.length > 0) {
2874
2867
  toolResults.forEach((tr) => modifiedHistory.push(tr));
2875
2868
  } else {
2876
- modifiedHistory.push({ role: "user", text: "[SYSTEM]: LOOP DETECTED by Internal System. If you have finished your task use [turn: finish] else continue." });
2869
+ modifiedHistory.push({ role: "user", text: "[SYSTEM]: LOOP DETECTED by Internal System. Your thinking process seems to be repeating and excreeding allocated budget for single turn. If you have finished your task use [turn: finish] else continue." });
2877
2870
  }
2878
2871
  }
2879
2872
  yield { type: "status", content: null };
@@ -3297,7 +3290,7 @@ Check what's new using \`/changelog\` command.`,
3297
3290
  const queuedPromptRef = useRef2(null);
3298
3291
  const [completedIndex, setCompletedIndex] = useState7(1);
3299
3292
  const windowedHistory = useMemo(() => {
3300
- const MAX_LINES = 1e3;
3293
+ const MAX_LINES = 2e3;
3301
3294
  const width = stdout?.columns || 80;
3302
3295
  let totalLines = 0;
3303
3296
  let startIdx = 0;
@@ -4687,7 +4680,7 @@ Selection: ${val}`,
4687
4680
  )))))));
4688
4681
  }
4689
4682
  };
4690
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", width: "100%" }, windowedHistory.isTruncated && /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "single", borderColor: "gray", paddingX: 1, marginBottom: 1, width: "100%", justifyContent: "center" }, /* @__PURE__ */ React10.createElement(Text10, { color: "gray", dimColor: true, italic: true }, "[ \u2191 History truncated for performance (showing last ~1000 lines) ]")), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", width: "100%", flexGrow: 1 }, windowedHistory.items.map((msg, idx) => /* @__PURE__ */ React10.createElement(MessageItem, { key: msg.id || idx, msg, showFullThinking, columns: stdout?.columns || 80 }))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1, width: "100%" }, (activeView === "chat" || ["ask", "approval", "terminalApproval"].includes(activeView)) && /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React10.createElement(
4683
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", width: "100%", flexGrow: 1 }, windowedHistory.items.map((msg, idx) => /* @__PURE__ */ React10.createElement(MessageItem, { key: msg.id || idx, msg, showFullThinking, columns: stdout?.columns || 80 }))), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1, width: "100%" }, (activeView === "chat" || ["ask", "approval", "terminalApproval"].includes(activeView)) && /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React10.createElement(
4691
4684
  ChatLayout_default,
4692
4685
  {
4693
4686
  messages: messages.slice(completedIndex),
@@ -4773,8 +4766,8 @@ var init_app = __esm({
4773
4766
  init_text();
4774
4767
  SESSION_START_TIME = Date.now();
4775
4768
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
4776
- versionFluxflow = "1.7.19";
4777
- updatedOn = "2026-05-06";
4769
+ versionFluxflow = "1.7.20";
4770
+ updatedOn = "2026-05-07";
4778
4771
  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 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(
4779
4772
  CommandMenu,
4780
4773
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.7.19",
3
+ "version": "1.7.20",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",