fluxflow-cli 2.13.1 → 2.13.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.
Files changed (2) hide show
  1. package/dist/fluxflow.js +451 -232
  2. package/package.json +4 -3
package/dist/fluxflow.js CHANGED
@@ -2227,12 +2227,21 @@ var init_MultilineInput = __esm({
2227
2227
  const visibleLines = useMemo(() => {
2228
2228
  return visualLines.slice(newScrollOffset, newScrollOffset + visibleRows);
2229
2229
  }, [visualLines, newScrollOffset, visibleRows]);
2230
+ const [blink, setBlink] = useState2(true);
2231
+ useEffect2(() => {
2232
+ setBlink(true);
2233
+ if (!focus || !showCursor) return;
2234
+ const timer = setInterval(() => {
2235
+ setBlink((prev) => !prev);
2236
+ }, 530);
2237
+ return () => clearInterval(timer);
2238
+ }, [focus, showCursor, value, cursorIndex]);
2230
2239
  const cursorStyle = useMemo(() => ({
2231
2240
  ...textStyle,
2232
- color: showCursor && focus ? "white" : void 0,
2233
- bold: showCursor && focus,
2234
- inverse: showCursor && focus
2235
- }), [textStyle, showCursor, focus]);
2241
+ color: showCursor && focus && blink ? "white" : void 0,
2242
+ bold: showCursor && focus && blink,
2243
+ inverse: showCursor && focus && blink
2244
+ }), [textStyle, showCursor, focus, blink]);
2236
2245
  return /* @__PURE__ */ React2.createElement(Box, { height: visibleRows, width: wrapWidth, overflow: "hidden", flexDirection: "column", flexGrow: 0, flexShrink: 0 }, visibleLines.map((lineObj, idx) => {
2237
2246
  const globalLineIdx = newScrollOffset + idx;
2238
2247
  const isCursorLine = globalLineIdx === cursorLine && focus && showCursor;
@@ -2316,6 +2325,16 @@ var init_MultilineInput = __esm({
2316
2325
  cursorIndexRef.current = newIndex;
2317
2326
  setCursorIndex(newIndex);
2318
2327
  setPasteLength(0);
2328
+ } else if (key.upArrow && cursorLine === 0) {
2329
+ cursorIndexRef.current = 0;
2330
+ setCursorIndex(0);
2331
+ setPasteLength(0);
2332
+ } else if (key.downArrow && cursorLine === visualLines.length - 1) {
2333
+ const lastLineObj = visualLines[visualLines.length - 1];
2334
+ const newIndex = lastLineObj.globalStart + lastLineObj.text.length;
2335
+ cursorIndexRef.current = newIndex;
2336
+ setCursorIndex(newIndex);
2337
+ setPasteLength(0);
2319
2338
  }
2320
2339
  }
2321
2340
  } else if (key.leftArrow) {
@@ -2346,6 +2365,22 @@ var init_MultilineInput = __esm({
2346
2365
  onChange(newValue);
2347
2366
  setPasteLength(0);
2348
2367
  }
2368
+ } else if (key.home || key.end) {
2369
+ if (showCursor) {
2370
+ const { visualLines, cursorLine } = computeVisualMatrix(val, curIdx, wrapWidth, identity);
2371
+ const currentLineObj = visualLines[cursorLine];
2372
+ if (currentLineObj) {
2373
+ let newIndex;
2374
+ if (key.home) {
2375
+ newIndex = currentLineObj.globalStart;
2376
+ } else if (key.end) {
2377
+ newIndex = currentLineObj.globalStart + currentLineObj.text.length;
2378
+ }
2379
+ cursorIndexRef.current = newIndex;
2380
+ setCursorIndex(newIndex);
2381
+ setPasteLength(0);
2382
+ }
2383
+ }
2349
2384
  } else {
2350
2385
  if (input) {
2351
2386
  const newValue = val.slice(0, curIdx) + input + val.slice(curIdx);
@@ -2741,6 +2776,7 @@ var init_text = __esm({
2741
2776
  text: line,
2742
2777
  gutterWidth,
2743
2778
  lineNum: idx + 1,
2779
+ isFirstLine: idx === 0,
2744
2780
  isLastLine: isLast
2745
2781
  };
2746
2782
  if (isLast && msg.isStreaming) {
@@ -2758,61 +2794,6 @@ var init_text = __esm({
2758
2794
  const match = text.match(/\[DIFF_START\]([\s\S]*?)(?:\[DIFF_END\]|$)/);
2759
2795
  const diffBody = match ? match[1].trim() : "";
2760
2796
  const diffLines = diffBody.split("\n").map((l) => l.replace(/\r$/, ""));
2761
- const highlightInfos = Array(diffLines.length).fill(null);
2762
- let idx = 0;
2763
- while (idx < diffLines.length) {
2764
- const removals = [];
2765
- const additions = [];
2766
- while (idx < diffLines.length) {
2767
- const line = diffLines[idx];
2768
- const cleanLine = line.replace("[UI_CONTEXT]", "");
2769
- if (cleanLine.startsWith("-")) {
2770
- removals.push({ idx, line: cleanLine });
2771
- idx++;
2772
- } else {
2773
- break;
2774
- }
2775
- }
2776
- while (idx < diffLines.length) {
2777
- const line = diffLines[idx];
2778
- const cleanLine = line.replace("[UI_CONTEXT]", "");
2779
- if (cleanLine.startsWith("+")) {
2780
- additions.push({ idx, line: cleanLine });
2781
- idx++;
2782
- } else {
2783
- break;
2784
- }
2785
- }
2786
- if (removals.length > 0 && additions.length > 0) {
2787
- const pairCount = Math.min(removals.length, additions.length);
2788
- for (let k = 0; k < pairCount; k++) {
2789
- const r = removals[k];
2790
- const a = additions[k];
2791
- const rRest = r.line.substring(1);
2792
- const rSplit = rRest.indexOf("|");
2793
- const rContent = rSplit !== -1 ? rRest.substring(rSplit + 1) : rRest;
2794
- const aRest = a.line.substring(1);
2795
- const aSplit = aRest.indexOf("|");
2796
- const aContent = aSplit !== -1 ? aRest.substring(aSplit + 1) : aRest;
2797
- let prefixLen = 0;
2798
- while (prefixLen < rContent.length && prefixLen < aContent.length && rContent[prefixLen] === aContent[prefixLen]) {
2799
- prefixLen++;
2800
- }
2801
- let suffixLen = 0;
2802
- const maxSuffix = Math.min(rContent.length - prefixLen, aContent.length - prefixLen);
2803
- while (suffixLen < maxSuffix && rContent[rContent.length - 1 - suffixLen] === aContent[aContent.length - 1 - suffixLen]) {
2804
- suffixLen++;
2805
- }
2806
- if (prefixLen > 0 || suffixLen > 0) {
2807
- highlightInfos[r.idx] = { prefixLen, suffixLen };
2808
- highlightInfos[a.idx] = { prefixLen, suffixLen };
2809
- }
2810
- }
2811
- }
2812
- if (removals.length === 0 && additions.length === 0) {
2813
- idx++;
2814
- }
2815
- }
2816
2797
  const completedBlocks2 = [];
2817
2798
  let activeBlock2 = null;
2818
2799
  diffLines.forEach((line, i) => {
@@ -2822,7 +2803,8 @@ var init_text = __esm({
2822
2803
  msg,
2823
2804
  type: "diff-line",
2824
2805
  text: line,
2825
- highlightInfo: highlightInfos[i]
2806
+ isFirstLine: i === 0,
2807
+ isLastLine: isLast
2826
2808
  };
2827
2809
  if (isLast && msg.isStreaming) {
2828
2810
  activeBlock2 = block;
@@ -2865,6 +2847,7 @@ var init_text = __esm({
2865
2847
  text: line
2866
2848
  };
2867
2849
  if (isLast && msg.isStreaming) {
2850
+ block.isActiveBlock = true;
2868
2851
  activeBlock = block;
2869
2852
  } else {
2870
2853
  completedBlocks.push(block);
@@ -2900,6 +2883,7 @@ var init_text = __esm({
2900
2883
  text: codeLines.join("\n")
2901
2884
  };
2902
2885
  if (isLast && msg.isStreaming && inCodeBlock) {
2886
+ block.isActiveBlock = true;
2903
2887
  activeBlock = block;
2904
2888
  } else {
2905
2889
  completedBlocks.push(block);
@@ -2918,6 +2902,7 @@ var init_text = __esm({
2918
2902
  text: codeLines.join("\n")
2919
2903
  };
2920
2904
  if (msg.isStreaming) {
2905
+ block.isActiveBlock = true;
2921
2906
  activeBlock = block;
2922
2907
  } else {
2923
2908
  completedBlocks.push(block);
@@ -2933,7 +2918,8 @@ var init_text = __esm({
2933
2918
  msg,
2934
2919
  type: "table",
2935
2920
  text: tableLines.join("\n"),
2936
- isStreaming: true
2921
+ isStreaming: true,
2922
+ isActiveBlock: true
2937
2923
  };
2938
2924
  } else {
2939
2925
  completedBlocks.push({
@@ -2964,6 +2950,7 @@ var init_text = __esm({
2964
2950
  text: line
2965
2951
  };
2966
2952
  if (isLast && msg.isStreaming) {
2953
+ block.isActiveBlock = true;
2967
2954
  activeBlock = block;
2968
2955
  } else {
2969
2956
  completedBlocks.push(block);
@@ -3497,12 +3484,77 @@ ${coloredArt[7]}`;
3497
3484
  // src/components/ChatLayout.jsx
3498
3485
  import React4, { useState as useState3, useEffect as useEffect3, useRef as useRef2 } from "react";
3499
3486
  import { Box as Box3, Text as Text4 } from "ink";
3500
- var formatThinkText, parseMathSymbols, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
3487
+ import { diffWordsWithSpace } from "diff";
3488
+ var useStreamingText, formatThinkText, parseMathSymbols, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, parseLineInfo, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
3501
3489
  var init_ChatLayout = __esm({
3502
3490
  "src/components/ChatLayout.jsx"() {
3503
3491
  init_TerminalBox();
3504
3492
  init_text();
3505
3493
  init_terminal();
3494
+ useStreamingText = (targetText, isStreaming, isActiveBlock) => {
3495
+ const [displayedText, setDisplayedText] = useState3(isActiveBlock && isStreaming ? "" : targetText);
3496
+ const targetTextRef = useRef2(targetText);
3497
+ useEffect3(() => {
3498
+ targetTextRef.current = targetText;
3499
+ }, [targetText]);
3500
+ useEffect3(() => {
3501
+ if (!isActiveBlock) {
3502
+ setDisplayedText(targetText);
3503
+ return;
3504
+ }
3505
+ if (!isStreaming && displayedText === targetText) {
3506
+ return;
3507
+ }
3508
+ const interval = setInterval(() => {
3509
+ setDisplayedText((current) => {
3510
+ const target = targetTextRef.current;
3511
+ if (current.length >= target.length) {
3512
+ if (!isStreaming) {
3513
+ clearInterval(interval);
3514
+ }
3515
+ return current;
3516
+ }
3517
+ if (!target.startsWith(current)) {
3518
+ return target;
3519
+ }
3520
+ const remaining = target.substring(current.length);
3521
+ const words = remaining.split(/(\s+)/);
3522
+ if (words.length <= 1) {
3523
+ if (!isStreaming) {
3524
+ clearInterval(interval);
3525
+ }
3526
+ return target;
3527
+ }
3528
+ const currentWordsCount = current.split(/\s+/).filter(Boolean).length;
3529
+ const targetWordsCount = target.split(/\s+/).filter(Boolean).length;
3530
+ const diff = targetWordsCount - currentWordsCount;
3531
+ let wordsToAdd = 1;
3532
+ if (diff > 15) {
3533
+ wordsToAdd = 4;
3534
+ } else if (diff > 8) {
3535
+ wordsToAdd = 3;
3536
+ } else if (diff > 3) {
3537
+ wordsToAdd = 2;
3538
+ }
3539
+ let addedText = "";
3540
+ let wordCount = 0;
3541
+ for (let i = 0; i < words.length; i++) {
3542
+ const w = words[i];
3543
+ addedText += w;
3544
+ if (/\S/.test(w)) {
3545
+ wordCount++;
3546
+ if (wordCount >= wordsToAdd) {
3547
+ break;
3548
+ }
3549
+ }
3550
+ }
3551
+ return current + addedText;
3552
+ });
3553
+ }, 100);
3554
+ return () => clearInterval(interval);
3555
+ }, [isStreaming, isActiveBlock, targetText, displayedText]);
3556
+ return displayedText;
3557
+ };
3506
3558
  formatThinkText = (cleaned, columns = 80) => {
3507
3559
  if (!cleaned) return null;
3508
3560
  const availableWidth = columns - 10;
@@ -3686,97 +3738,95 @@ var init_ChatLayout = __esm({
3686
3738
  flushBuffers("final");
3687
3739
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 2 }, result);
3688
3740
  });
3689
- DiffLine = React4.memo(({ line, columns = 80, highlightInfo }) => {
3741
+ parseLineInfo = (l) => {
3742
+ if (!l) return null;
3743
+ const clean = l.replace("[UI_CONTEXT]", "").replace(/\r/g, "");
3744
+ const isR = clean.startsWith("-");
3745
+ const isA = clean.startsWith("+");
3746
+ let rest = isR || isA ? clean.substring(1) : clean;
3747
+ rest = rest.trim();
3748
+ const splitIdx = rest.indexOf("|");
3749
+ const num = splitIdx !== -1 ? rest.substring(0, splitIdx).trim() : "";
3750
+ const content = splitIdx !== -1 ? rest.substring(splitIdx + 1) : rest;
3751
+ return { isR, isA, num, content };
3752
+ };
3753
+ DiffLine = React4.memo(({ line, pairContent, parentText, columns = 80 }) => {
3690
3754
  const isContext = line.includes("[UI_CONTEXT]");
3691
3755
  const cleanLine = line.replace("[UI_CONTEXT]", "");
3692
3756
  if (isContext && cleanLine.includes("\u2550")) {
3693
3757
  return /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2550".repeat(Math.max(10, columns - 4))));
3694
3758
  }
3695
- const isRemoval = cleanLine.startsWith("-");
3696
- const isAddition = cleanLine.startsWith("+");
3697
- const prefixChar = cleanLine[0];
3698
- const rest = cleanLine.substring(1);
3699
- const splitIdx = rest.indexOf("|");
3700
- const lineNum = splitIdx !== -1 ? rest.substring(0, splitIdx).trim() : "";
3701
- const content = splitIdx !== -1 ? rest.substring(splitIdx + 1) : rest;
3702
- const bgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : "#1a1a1a";
3703
- const textColor = isRemoval ? "#ff4d4d" : isAddition ? "#4dff88" : isContext ? "white" : "white";
3704
- const numColor = isRemoval ? "#cf3a3a" : isAddition ? "#3acf65" : "gray";
3705
- const hasHighlight = highlightInfo && (highlightInfo.prefixLen > 0 || highlightInfo.suffixLen > 0);
3706
- const rowBgColor = hasHighlight ? "#1a1a1a" : bgColor;
3707
- if (hasHighlight) {
3708
- const prefixLen = highlightInfo.prefixLen;
3709
- const suffixLen = highlightInfo.suffixLen;
3710
- const prefix = content.substring(0, prefixLen);
3711
- const delta = content.substring(prefixLen, content.length - suffixLen);
3712
- const suffix = content.substring(content.length - suffixLen);
3713
- return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: rowBgColor, paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0, justifyContent: "flex-end" }, /* @__PURE__ */ React4.createElement(Text4, { color: numColor, dimColor: isContext }, lineNum)), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, bold: true }, isRemoval ? "-" : isAddition ? "+" : " ")), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, dimColor: isContext }, prefix, /* @__PURE__ */ React4.createElement(Text4, { backgroundColor: bgColor }, delta), suffix)));
3714
- }
3715
- return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: rowBgColor, paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0, justifyContent: "flex-end" }, /* @__PURE__ */ React4.createElement(Text4, { color: numColor, dimColor: isContext }, lineNum)), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, bold: true }, isRemoval ? "-" : isAddition ? "+" : " ")), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, dimColor: isContext }, wrapText(content, columns - 14))));
3759
+ const parsedCurrent = parseLineInfo(line);
3760
+ if (!parsedCurrent) {
3761
+ return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, wrapText(cleanLine, columns - 14))));
3762
+ }
3763
+ const { isR: isRemoval, isA: isAddition, num: lineNum, content } = parsedCurrent;
3764
+ const innerBgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : void 0;
3765
+ let finalPairContent = pairContent;
3766
+ if (!finalPairContent && parentText && (isRemoval || isAddition)) {
3767
+ const cleanParent = parentText.replace(/\[DIFF_START\]|\[DIFF_END\]/g, "").trim();
3768
+ const diffLines = cleanParent.split("\n");
3769
+ const pairLine = diffLines.find((l) => {
3770
+ const p = parseLineInfo(l);
3771
+ return p && p.num === lineNum && p.isR !== isRemoval;
3772
+ });
3773
+ if (pairLine) {
3774
+ finalPairContent = parseLineInfo(pairLine).content;
3775
+ }
3776
+ }
3777
+ let words = [];
3778
+ if (finalPairContent !== void 0 && finalPairContent !== null) {
3779
+ const oldStr = isRemoval ? content : finalPairContent;
3780
+ const newStr = isRemoval ? finalPairContent : content;
3781
+ try {
3782
+ words = diffWordsWithSpace(oldStr, newStr);
3783
+ } catch (e) {
3784
+ words = [];
3785
+ }
3786
+ }
3787
+ const hasInlineChange = words.some((part) => isRemoval && part.removed || isAddition && part.added);
3788
+ const isPureUnpairedBlock = !finalPairContent && (isRemoval || isAddition);
3789
+ const hasRealChange = hasInlineChange || isPureUnpairedBlock;
3790
+ const finalNumColor = isRemoval || isAddition ? isRemoval ? "#d96868" : "#68d98c" : "gray";
3791
+ const finalPrefixColor = isRemoval ? "#ff4d4d" : "#4dff88";
3792
+ const displayPrefix = isRemoval ? "-" : isAddition ? "+" : " ";
3793
+ const renderInlineDiff = () => {
3794
+ if (isPureUnpairedBlock) {
3795
+ const blockColor = isRemoval ? "#ff3333" : "#33ff66";
3796
+ return /* @__PURE__ */ React4.createElement(Text4, { color: blockColor }, wrapText(content, columns - 14));
3797
+ }
3798
+ if (!(isRemoval || isAddition) || words.length === 0 || !hasInlineChange) {
3799
+ const textColor = isRemoval ? "#b34d4d" : isAddition ? "#4db36b" : "gray";
3800
+ return /* @__PURE__ */ React4.createElement(Text4, { color: textColor }, wrapText(content, columns - 14));
3801
+ }
3802
+ return /* @__PURE__ */ React4.createElement(Text4, { wrap: "anywhere" }, words.map((part, idx) => {
3803
+ const isWhitespace = /^\s+$/.test(part.value);
3804
+ if (isRemoval) {
3805
+ const isSurroundedByRemoval = words[idx - 1]?.removed || words[idx + 1]?.removed;
3806
+ if (part.removed || isWhitespace && isSurroundedByRemoval) {
3807
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#ff3333" }, part.value);
3808
+ }
3809
+ if (part.added) return null;
3810
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#b34d4d" }, part.value);
3811
+ }
3812
+ if (isAddition) {
3813
+ const isSurroundedByAddition = words[idx - 1]?.added || words[idx + 1]?.added;
3814
+ if (part.added || isWhitespace && isSurroundedByAddition) {
3815
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#33ff66" }, part.value);
3816
+ }
3817
+ if (part.removed) return null;
3818
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#4db36b" }, part.value);
3819
+ }
3820
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "gray" }, part.value);
3821
+ }));
3822
+ };
3823
+ return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0, justifyContent: "flex-end" }, /* @__PURE__ */ React4.createElement(Text4, { color: finalNumColor }, lineNum)), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: finalPrefixColor }, displayPrefix)), /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 1, backgroundColor: innerBgColor, flexShrink: 1 }, renderInlineDiff()));
3716
3824
  });
3717
3825
  DiffBlock = React4.memo(({ text, columns = 80 }) => {
3718
3826
  const match = text.match(/\[DIFF_START\]([\s\S]*?)\[DIFF_END\]/);
3719
3827
  const diffBody = match ? match[1].trim() : "";
3720
3828
  const diffLines = diffBody.split("\n");
3721
- const highlightInfos = React4.useMemo(() => {
3722
- const infos = Array(diffLines.length).fill(null);
3723
- let idx = 0;
3724
- while (idx < diffLines.length) {
3725
- const removals = [];
3726
- const additions = [];
3727
- while (idx < diffLines.length) {
3728
- const line = diffLines[idx];
3729
- const cleanLine = line.replace("[UI_CONTEXT]", "");
3730
- if (cleanLine.startsWith("-")) {
3731
- removals.push({ idx, line: cleanLine });
3732
- idx++;
3733
- } else {
3734
- break;
3735
- }
3736
- }
3737
- while (idx < diffLines.length) {
3738
- const line = diffLines[idx];
3739
- const cleanLine = line.replace("[UI_CONTEXT]", "");
3740
- if (cleanLine.startsWith("+")) {
3741
- additions.push({ idx, line: cleanLine });
3742
- idx++;
3743
- } else {
3744
- break;
3745
- }
3746
- }
3747
- if (removals.length > 0 && additions.length > 0) {
3748
- const pairCount = Math.min(removals.length, additions.length);
3749
- for (let k = 0; k < pairCount; k++) {
3750
- const r = removals[k];
3751
- const a = additions[k];
3752
- const rRest = r.line.substring(1);
3753
- const rSplit = rRest.indexOf("|");
3754
- const rContent = rSplit !== -1 ? rRest.substring(rSplit + 1) : rRest;
3755
- const aRest = a.line.substring(1);
3756
- const aSplit = aRest.indexOf("|");
3757
- const aContent = aSplit !== -1 ? aRest.substring(aSplit + 1) : aRest;
3758
- let prefixLen = 0;
3759
- while (prefixLen < rContent.length && prefixLen < aContent.length && rContent[prefixLen] === aContent[prefixLen]) {
3760
- prefixLen++;
3761
- }
3762
- let suffixLen = 0;
3763
- const maxSuffix = Math.min(rContent.length - prefixLen, aContent.length - prefixLen);
3764
- while (suffixLen < maxSuffix && rContent[rContent.length - 1 - suffixLen] === aContent[aContent.length - 1 - suffixLen]) {
3765
- suffixLen++;
3766
- }
3767
- if (prefixLen > 0 || suffixLen > 0) {
3768
- infos[r.idx] = { prefixLen, suffixLen };
3769
- infos[a.idx] = { prefixLen, suffixLen };
3770
- }
3771
- }
3772
- }
3773
- if (removals.length === 0 && additions.length === 0) {
3774
- idx++;
3775
- }
3776
- }
3777
- return infos;
3778
- }, [diffLines]);
3779
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 3, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingY: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 5, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 2, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " "))), diffLines.map((line, i) => /* @__PURE__ */ React4.createElement(DiffLine, { key: i, line, columns: columns - 3, highlightInfo: highlightInfos[i] })), /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 5, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 2, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " ")))));
3829
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 3, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingY: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " "))), diffLines.map((line, i) => /* @__PURE__ */ React4.createElement(DiffLine, { key: i, line, parentText: text, columns: columns - 3 })), /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " ")))));
3780
3830
  });
3781
3831
  CodeRenderer = React4.memo(({ text, columns = 80 }) => {
3782
3832
  if (!text) return null;
@@ -3895,13 +3945,70 @@ var init_ChatLayout = __esm({
3895
3945
  const selectionMatch = msg.text.match(/Selection: (.*)/);
3896
3946
  const selection = selectionMatch ? selectionMatch[1] : "No selection";
3897
3947
  const s = emojiSpace(2);
3898
- return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "AGENT REQUEST: RESOLVED")), /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white" }, "Selection: ", /* @__PURE__ */ React4.createElement(Text4, { color: "grey", bold: true }, selection)))));
3948
+ return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(
3949
+ Box3,
3950
+ {
3951
+ flexDirection: "column",
3952
+ borderStyle: "single",
3953
+ borderLeft: true,
3954
+ borderRight: false,
3955
+ borderTop: false,
3956
+ borderBottom: false,
3957
+ borderColor: "#444444",
3958
+ paddingLeft: 2,
3959
+ paddingRight: 0,
3960
+ paddingTop: 1,
3961
+ paddingBottom: 1,
3962
+ backgroundColor: "#1a1a1a",
3963
+ width: "100%"
3964
+ },
3965
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "AGENT REQUEST: RESOLVED")),
3966
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white" }, "Selection: ", /* @__PURE__ */ React4.createElement(Text4, { color: "grey", bold: true }, selection)))
3967
+ ));
3899
3968
  }
3900
3969
  if (msg.isAboutRecord) {
3901
- return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "ABOUT FLUX FLOW")), /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, msg.text))));
3970
+ return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(
3971
+ Box3,
3972
+ {
3973
+ flexDirection: "column",
3974
+ borderStyle: "single",
3975
+ borderLeft: true,
3976
+ borderRight: false,
3977
+ borderTop: false,
3978
+ borderBottom: false,
3979
+ borderColor: "#444444",
3980
+ paddingLeft: 2,
3981
+ paddingRight: 0,
3982
+ paddingTop: 1,
3983
+ paddingBottom: 1,
3984
+ backgroundColor: "#1a1a1a",
3985
+ width: "100%"
3986
+ },
3987
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "ABOUT FLUX FLOW")),
3988
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, msg.text))
3989
+ ));
3902
3990
  }
3903
3991
  if (msg.isUpdateNotification) {
3904
- return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "UPDATE AVAILABLE")), /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text: msg.text, columns }))));
3992
+ return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(
3993
+ Box3,
3994
+ {
3995
+ flexDirection: "column",
3996
+ borderStyle: "single",
3997
+ borderLeft: true,
3998
+ borderRight: false,
3999
+ borderTop: false,
4000
+ borderBottom: false,
4001
+ borderColor: "#444444",
4002
+ paddingLeft: 2,
4003
+ paddingRight: 0,
4004
+ paddingTop: 1,
4005
+ paddingBottom: 1,
4006
+ backgroundColor: "#1a1a1a",
4007
+ width: "100%"
4008
+ },
4009
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "UPDATE AVAILABLE")),
4010
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text: msg.text, columns }))
4011
+ ));
3905
4012
  }
3906
4013
  if (msg.isHelpRecord) {
3907
4014
  const commandList = [
@@ -3994,17 +4101,18 @@ var init_ChatLayout = __esm({
3994
4101
  if (!text || text.trim() === "") {
3995
4102
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "row", width: "100%", paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2502 "));
3996
4103
  }
3997
- const trimmed = text.trim();
4104
+ const animatedText = useStreamingText(text, msg.isStreaming, block.isActiveBlock);
4105
+ const trimmed = animatedText.trim();
3998
4106
  const isUnordered = /^[\*\-\+]\s/.test(trimmed);
3999
4107
  const isOrdered = /^\d+\.\s/.test(trimmed);
4000
- let content = text;
4108
+ let content = animatedText;
4001
4109
  if (isUnordered || isOrdered) {
4002
4110
  const bullet = isUnordered ? " \u2022 " : trimmed.match(/^\d+\.\s/)[0];
4003
4111
  const indent = " ".repeat(bullet.length);
4004
4112
  const wrappedPart = wrapText(trimmed.replace(/^[\*\-\d+\.]+\s/, ""), columns - (bullet.length + 10));
4005
4113
  content = bullet + wrappedPart.split("\n").join("\n" + indent);
4006
4114
  } else {
4007
- content = wrapText(text, columns - 10);
4115
+ content = wrapText(animatedText, columns - 10);
4008
4116
  }
4009
4117
  const wrappedLines = content.split("\n");
4010
4118
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, wrappedLines.map((wLine, idx) => /* @__PURE__ */ React4.createElement(Box3, { key: idx, flexDirection: "row", width: "100%" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2502 "), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(InlineMarkdown, { text: wLine, color: "gray", italic: true })))));
@@ -4017,7 +4125,8 @@ var init_ChatLayout = __esm({
4017
4125
  if (!text || text.trim() === "") {
4018
4126
  return /* @__PURE__ */ React4.createElement(Box3, { height: 1 });
4019
4127
  }
4020
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text, columns }));
4128
+ const animatedText = useStreamingText(text, msg.isStreaming, block.isActiveBlock);
4129
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text: animatedText, columns }));
4021
4130
  }
4022
4131
  if (type === "table") {
4023
4132
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(TableRenderer, { buffer: text.split("\n"), terminalWidth: columns }));
@@ -4029,8 +4138,9 @@ var init_ChatLayout = __esm({
4029
4138
  DiffLine,
4030
4139
  {
4031
4140
  line: text,
4032
- columns,
4033
- highlightInfo: block.highlightInfo
4141
+ pairContent: block.pairContent,
4142
+ parentText: msg?.text,
4143
+ columns
4034
4144
  }
4035
4145
  ), isLastLine && renderPaddingLine(true));
4036
4146
  }
@@ -4342,7 +4452,8 @@ var init_main_tools = __esm({
4342
4452
  };
4343
4453
  TOOL_PROTOCOL = (mode, osDetected, isMultiModal, aiProvider) => `
4344
4454
  -- TOOL DEFINITIONS --
4345
- Internal tools. MUST use the EXACT syntax '[tool:functions.ToolName(args)]'. **NO OTHER SYNTAX/MARKERS ALLOWED, BRACKETS SHOULD BE PROPERLY USED AS PER SCHEMA**
4455
+ Internal tools. **MUST use the EXACT syntax** [tool:functions.ToolName(args)]. **NO OTHER SYNTAX/MARKERS/BOUNDARY ALLOWED**
4456
+ NO TOOL CALL INSIDE THINKING
4346
4457
 
4347
4458
  **TOOL USAGE POLICY:**
4348
4459
  - **MAX 3 TOOL CALLS PER TURN${mode === "Flux" ? " (EXCEPTION FOR Todo TOOL: 3+ CALLS ALLOWED, Run: Limit 1 OR 2 CONSECUTIVE Run)" : ""}. Next Turn, verify tool results, plan next**
@@ -5435,31 +5546,73 @@ var init_AskUserModal = __esm({
5435
5546
  });
5436
5547
  const s = emojiSpace(2);
5437
5548
  if (isSuggestingElse) {
5438
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4AC} SUGGEST SOMETHING ELSE")), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { italic: true, color: "gray" }, "Replying to: ", question)), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, flexDirection: "row" }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4A0} "), /* @__PURE__ */ React9.createElement(
5439
- TextInput3,
5440
- {
5441
- value: customInput,
5442
- onChange: setCustomInput,
5443
- onSubmit: () => onResolve(customInput)
5444
- }
5445
- )), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Press Enter to send)")));
5446
- }
5447
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "AGENT REQUEST: ACTION REQUIRED")), /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: "white" }, question)), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", width: "100%" }, allOptions.map((opt, idx) => {
5448
- const isSelected = idx === selectedIndex;
5449
5549
  return /* @__PURE__ */ React9.createElement(
5450
5550
  Box8,
5451
5551
  {
5452
- key: opt.id,
5453
5552
  flexDirection: "column",
5454
- width: "100%",
5455
- backgroundColor: isSelected ? "#2a2a2a" : void 0,
5456
- paddingX: 1,
5457
- marginBottom: idx === allOptions.length - 1 ? 0 : 1
5553
+ borderStyle: "single",
5554
+ borderLeft: true,
5555
+ borderRight: false,
5556
+ borderTop: false,
5557
+ borderBottom: false,
5558
+ borderColor: "#444444",
5559
+ paddingLeft: 2,
5560
+ paddingRight: 0,
5561
+ paddingTop: 1,
5562
+ paddingBottom: 1,
5563
+ backgroundColor: "#1a1a1a",
5564
+ width: "100%"
5458
5565
  },
5459
- /* @__PURE__ */ React9.createElement(Text9, { color: isSelected ? "white" : "grey", bold: isSelected }, isSelected ? "\u276F " : " ", opt.label),
5460
- opt.description && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 4 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, opt.description))
5566
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4AC} SUGGEST SOMETHING ELSE")),
5567
+ /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { italic: true, color: "gray" }, "Replying to: ", question)),
5568
+ /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, flexDirection: "row" }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4A0} "), /* @__PURE__ */ React9.createElement(
5569
+ TextInput3,
5570
+ {
5571
+ value: customInput,
5572
+ onChange: setCustomInput,
5573
+ onSubmit: () => onResolve(customInput)
5574
+ }
5575
+ )),
5576
+ /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Press Enter to send)"))
5461
5577
  );
5462
- })), /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Use Arrows to navigate, Enter to confirm)")));
5578
+ }
5579
+ return /* @__PURE__ */ React9.createElement(
5580
+ Box8,
5581
+ {
5582
+ flexDirection: "column",
5583
+ borderStyle: "single",
5584
+ borderLeft: true,
5585
+ borderRight: false,
5586
+ borderTop: false,
5587
+ borderBottom: false,
5588
+ borderColor: "#444444",
5589
+ paddingLeft: 2,
5590
+ paddingRight: 0,
5591
+ paddingTop: 1,
5592
+ paddingBottom: 1,
5593
+ backgroundColor: "#1a1a1a",
5594
+ width: "100%"
5595
+ },
5596
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "AGENT REQUEST: ACTION REQUIRED")),
5597
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: "white" }, question)),
5598
+ /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", width: "100%" }, allOptions.map((opt, idx) => {
5599
+ const isSelected = idx === selectedIndex;
5600
+ return /* @__PURE__ */ React9.createElement(
5601
+ Box8,
5602
+ {
5603
+ key: opt.id,
5604
+ flexDirection: "column",
5605
+ width: "100%",
5606
+ backgroundColor: isSelected ? "#2a2a2a" : void 0,
5607
+ paddingX: 1,
5608
+ marginBottom: idx === allOptions.length - 1 ? 0 : 1
5609
+ },
5610
+ /* @__PURE__ */ React9.createElement(Text9, { color: isSelected ? "white" : "grey", bold: isSelected }, isSelected ? "\u276F " : " ", opt.label),
5611
+ opt.description && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 4 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, opt.description))
5612
+ );
5613
+ })),
5614
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Use Arrows to navigate, Enter to confirm)"))
5615
+ );
5463
5616
  };
5464
5617
  AskUserModal_default = AskUserModal;
5465
5618
  }
@@ -5599,7 +5752,8 @@ ${projectContextBlock}
5599
5752
  - Temporal Awareness: RELATIVE TIME REFERENCE eg. few mins ago
5600
5753
 
5601
5754
  -- SECURITY RULES --${systemSettings.allowExternalAccess ? "" : "\n- ACCESS CONTROL: CWD only"}
5602
- - Sensitive files? Ask before Read${isSystemDir ? "\nPROTECTED DIRECTORY: ASK BEFORE MODIFYING" : ""}
5755
+ - Sensitive files? Ask before Read${isSystemDir ? "\n- PROTECTED DIRECTORY: ASK BEFORE MODIFYING" : ""}
5756
+ - No thinking leak in chat output
5603
5757
 
5604
5758
  -- FORMATTING --
5605
5759
  - GFM Supported
@@ -8334,16 +8488,62 @@ var init_todo = __esm({
8334
8488
  return `- [ ] ${trimmed}`;
8335
8489
  }).filter(Boolean).join("\n") + "\n";
8336
8490
  };
8491
+ const applyMarkDone = (content, markDone2) => {
8492
+ if (!markDone2) return { content, markedCount: 0 };
8493
+ const rawTargets = parseMessyArray(markDone2);
8494
+ const targets = (Array.isArray(rawTargets) ? rawTargets : [rawTargets]).map((t) => String(t).replace(/^- \[[xX ]\]\s*/i, "").trim()).filter(Boolean);
8495
+ const lines = content.split("\n");
8496
+ let markedCount = 0;
8497
+ let fileUpdated = false;
8498
+ for (const searchStr of targets) {
8499
+ let updatedThisTarget = false;
8500
+ for (let i = 0; i < lines.length; i++) {
8501
+ if (lines[i].includes(searchStr) && /^- \[\s\]/.test(lines[i].trim())) {
8502
+ lines[i] = lines[i].replace("- [ ]", "- [x]");
8503
+ updatedThisTarget = true;
8504
+ fileUpdated = true;
8505
+ markedCount++;
8506
+ break;
8507
+ }
8508
+ }
8509
+ if (!updatedThisTarget) {
8510
+ for (let i = 0; i < lines.length; i++) {
8511
+ if (lines[i].toLowerCase().includes(searchStr.toLowerCase()) && /^- \[\s\]/.test(lines[i].trim())) {
8512
+ lines[i] = lines[i].replace("- [ ]", "- [x]");
8513
+ updatedThisTarget = true;
8514
+ fileUpdated = true;
8515
+ markedCount++;
8516
+ break;
8517
+ }
8518
+ }
8519
+ }
8520
+ }
8521
+ return {
8522
+ content: fileUpdated ? lines.join("\n") : content,
8523
+ markedCount
8524
+ };
8525
+ };
8337
8526
  try {
8338
8527
  if (!fs19.existsSync(todoDir)) {
8339
8528
  fs19.mkdirSync(todoDir, { recursive: true });
8340
8529
  }
8341
8530
  if (method === "create") {
8342
8531
  if (!tasks) return 'ERROR: Missing "tasks" for create method.';
8343
- const content = getTasksString(tasks);
8532
+ let content = getTasksString(tasks);
8533
+ let markedCount = 0;
8534
+ if (markDone) {
8535
+ const result = applyMarkDone(content, markDone);
8536
+ content = result.content;
8537
+ markedCount = result.markedCount;
8538
+ }
8344
8539
  await RevertManager.recordFileChange(todoFile);
8345
8540
  fs19.writeFileSync(todoFile, content, "utf8");
8346
8541
  const total = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.startsWith("- [ ]") || l.startsWith("- [x]") || l.startsWith("- [X]")).length;
8542
+ if (markedCount > 0) {
8543
+ const completed = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.startsWith("- [x]") || l.startsWith("- [X]")).length;
8544
+ return `SUCCESS: TASK LIST CREATED (${markedCount} marked done, ${completed} completed, ${total - completed} left)
8545
+ ${content}`;
8546
+ }
8347
8547
  return `SUCCESS: TASK LIST CREATED (${total} total)
8348
8548
  ${content}`;
8349
8549
  }
@@ -8367,35 +8567,10 @@ ${fullContent}`;
8367
8567
  let content = fs19.readFileSync(todoFile, "utf8");
8368
8568
  let markedCount = 0;
8369
8569
  if (markDone) {
8370
- const rawTargets = parseMessyArray(markDone);
8371
- const targets = (Array.isArray(rawTargets) ? rawTargets : [rawTargets]).map((t) => String(t).replace(/^- \[[xX ]\]\s*/i, "").trim()).filter(Boolean);
8372
- const lines = content.split("\n");
8373
- let fileUpdated = false;
8374
- for (const searchStr of targets) {
8375
- let updatedThisTarget = false;
8376
- for (let i = 0; i < lines.length; i++) {
8377
- if (lines[i].includes(searchStr) && /^- \[\s\]/.test(lines[i].trim())) {
8378
- lines[i] = lines[i].replace("- [ ]", "- [x]");
8379
- updatedThisTarget = true;
8380
- fileUpdated = true;
8381
- markedCount++;
8382
- break;
8383
- }
8384
- }
8385
- if (!updatedThisTarget) {
8386
- for (let i = 0; i < lines.length; i++) {
8387
- if (lines[i].toLowerCase().includes(searchStr.toLowerCase()) && /^- \[\s\]/.test(lines[i].trim())) {
8388
- lines[i] = lines[i].replace("- [ ]", "- [x]");
8389
- updatedThisTarget = true;
8390
- fileUpdated = true;
8391
- markedCount++;
8392
- break;
8393
- }
8394
- }
8395
- }
8396
- }
8397
- if (fileUpdated) {
8398
- content = lines.join("\n");
8570
+ const result = applyMarkDone(content, markDone);
8571
+ if (result.markedCount > 0) {
8572
+ content = result.content;
8573
+ markedCount = result.markedCount;
8399
8574
  await RevertManager.recordFileChange(todoFile);
8400
8575
  fs19.writeFileSync(todoFile, content, "utf8");
8401
8576
  }
@@ -10449,8 +10624,8 @@ ${ideCtx.warnings}
10449
10624
  const boxTop = `${" ".repeat(boxWidth)}`;
10450
10625
  const boxMid = boxLines.map((line) => `${line.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`).join("\n");
10451
10626
  const boxBottom = `${" ".repeat(boxWidth)}`;
10452
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}
10453
- ${boxBottom}`) };
10627
+ yield { type: "visual_feedback", content: colorMainWords(`${boxBottom}
10628
+ ${boxMid}`) };
10454
10629
  continue;
10455
10630
  }
10456
10631
  const finalStart = startLine !== null ? startLine : 1;
@@ -10507,8 +10682,8 @@ ${boxBottom}`) };
10507
10682
  const boxWidth = Math.min(maxLen + 4, terminalWidth);
10508
10683
  const boxMid = boxLines.map((line) => `${line.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`).join("\n");
10509
10684
  const boxBottom = `${" ".repeat(boxWidth)}`;
10510
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}
10511
- ${boxBottom}`) };
10685
+ yield { type: "visual_feedback", content: colorMainWords(`${boxBottom}
10686
+ ${boxMid}`) };
10512
10687
  }
10513
10688
  }
10514
10689
  }
@@ -11462,8 +11637,8 @@ ${ideErr} [/ERROR]`;
11462
11637
  const boxWidth = Math.min(deniedLabel.length + 4, terminalWidth);
11463
11638
  const boxMid = `${deniedLabel.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`;
11464
11639
  const boxBottom = ` ${" ".repeat(boxWidth)} `;
11465
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}
11466
- ${boxBottom}`) };
11640
+ yield { type: "visual_feedback", content: colorMainWords(`${boxBottom}
11641
+ ${boxMid}`) };
11467
11642
  }
11468
11643
  toolResults.push({ role: "user", text: `[TOOL RESULT]: ERROR: ${denyMsg}` });
11469
11644
  yield { type: "tool_result", content: `[TOOL RESULT]: ERROR: ${denyMsg}` };
@@ -11686,8 +11861,8 @@ ${failures.map((f) => ` \u2022 ${f.error}`).join("\n")}`;
11686
11861
  const boxWidth = Math.min(errorLabel.length + 4, terminalWidth);
11687
11862
  const boxMid = `${errorLabel.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`;
11688
11863
  const boxBottom = ` ${" ".repeat(boxWidth)} `;
11689
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}
11690
- ${boxBottom}`) };
11864
+ yield { type: "visual_feedback", content: colorMainWords(`${boxBottom}
11865
+ ${boxMid}}`) };
11691
11866
  toolResults.push({ role: "user", text: errorMsg });
11692
11867
  await incrementUsage("toolFailure");
11693
11868
  if (settings.onToolResult) settings.onToolResult("failure", normToolName);
@@ -11837,7 +12012,8 @@ ${snippet2}
11837
12012
  }
11838
12013
  const boxWidth = Math.min(feedbackLabel.length + 4, terminalWidth);
11839
12014
  const boxMid = `${feedbackLabel.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`;
11840
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}`) };
12015
+ yield { type: "visual_feedback", content: colorMainWords(`
12016
+ ${boxMid}`) };
11841
12017
  const toolEnd2 = Date.now();
11842
12018
  lastToolFinishedAt = toolEnd2;
11843
12019
  yield { type: "tool_time", content: toolEnd2 - executionStart };
@@ -11871,8 +12047,8 @@ ${snippet2}
11871
12047
  const boxWidth = Math.min(deniedLabel.length + 4, terminalWidth);
11872
12048
  const boxMid = `${deniedLabel.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`;
11873
12049
  const boxBottom = ` ${" ".repeat(boxWidth)} `;
11874
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}
11875
- ${boxBottom}`) };
12050
+ yield { type: "visual_feedback", content: colorMainWords(`${boxBottom}
12051
+ ${boxMid}`) };
11876
12052
  }
11877
12053
  if (normToolName === "exec_command") {
11878
12054
  await new Promise((resolve) => setTimeout(resolve, 50));
@@ -11897,7 +12073,8 @@ ${boxBottom}`) };
11897
12073
  const boxWidth = Math.min(label2.length + 4, terminalWidth);
11898
12074
  const boxMid = `${label2.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`;
11899
12075
  const boxBottom = ` ${" ".repeat(boxWidth)} `;
11900
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}${boxMid.includes("Created") || boxMid.includes("Edited") || boxMid.includes("Written") ? "" : `
12076
+ yield { type: "visual_feedback", content: colorMainWords(`
12077
+ ${boxMid}${boxMid.includes("Created") || boxMid.includes("Edited") || boxMid.includes("Written") ? "" : `
11901
12078
  ${boxBottom}`}`) };
11902
12079
  }
11903
12080
  if (lastToolFinishedAt > 0) {
@@ -11967,8 +12144,8 @@ ${boxBottom}`}`) };
11967
12144
  const boxWidth = Math.min(postLabel.length + 4, terminalWidth);
11968
12145
  const boxMid = `${postLabel.padEnd(boxWidth - 2).substring(0, boxWidth - 2)}`;
11969
12146
  const boxBottom = ` ${" ".repeat(boxWidth)} `;
11970
- yield { type: "visual_feedback", content: colorMainWords(`${boxMid}
11971
- ${boxBottom}`) };
12147
+ yield { type: "visual_feedback", content: colorMainWords(`${boxBottom}
12148
+ ${boxMid}`) };
11972
12149
  }
11973
12150
  if (normToolName === "todo") {
11974
12151
  const { method, tasks, markDone } = parseArgs(toolCall.args);
@@ -12021,7 +12198,8 @@ ${boxBottom}`) };
12021
12198
  ""
12022
12199
  // Bottom padding spacing
12023
12200
  ].join("\n");
12024
- yield { type: "visual_feedback", content: colorMainWords(output) };
12201
+ yield { type: "visual_feedback", content: `
12202
+ ${colorMainWords(output)}` };
12025
12203
  }
12026
12204
  }
12027
12205
  if (normToolName === "exec_command" && settings.onExecEnd) {
@@ -13915,18 +14093,50 @@ function App({ args = [] }) {
13915
14093
  const completed = [];
13916
14094
  const active = [];
13917
14095
  const columns = terminalSize.columns || 80;
13918
- messages.slice(0, completedIndex).forEach((msg) => {
14096
+ const completedMsgs = messages.slice(0, completedIndex);
14097
+ const activeMsgs = messages.slice(completedIndex);
14098
+ const seenAskSelections = /* @__PURE__ */ new Set();
14099
+ const filterDuplicates = (msgList) => {
14100
+ return msgList.filter((msg) => {
14101
+ if (msg.isAskRecord) {
14102
+ const selectionMatch = msg.text?.match(/Selection: (.*)/);
14103
+ const selection = selectionMatch ? selectionMatch[1].trim() : "";
14104
+ if (selection) {
14105
+ if (seenAskSelections.has(selection)) {
14106
+ return false;
14107
+ }
14108
+ seenAskSelections.add(selection);
14109
+ }
14110
+ }
14111
+ return true;
14112
+ });
14113
+ };
14114
+ const uniqueCompleted = filterDuplicates(completedMsgs);
14115
+ const uniqueActive = filterDuplicates(activeMsgs);
14116
+ uniqueCompleted.forEach((msg) => {
13919
14117
  const parsed = parseMessageToBlocks(msg, columns);
13920
14118
  completed.push(...parsed.completed);
13921
14119
  completed.push(...parsed.active);
13922
14120
  });
13923
- messages.slice(completedIndex).forEach((msg) => {
14121
+ uniqueActive.forEach((msg) => {
13924
14122
  const parsed = parseMessageToBlocks(msg, columns);
13925
14123
  completed.push(...parsed.completed);
13926
14124
  active.push(...parsed.active);
13927
14125
  });
13928
- const MAX_BLOCKS = 1e3;
14126
+ const MAX_BLOCKS = 5e9;
13929
14127
  const slicedCompleted = completed.slice(Math.max(0, completed.length - MAX_BLOCKS));
14128
+ if (slicedCompleted.length >= 75e3) {
14129
+ slicedCompleted.push({
14130
+ key: "memory-warning-block",
14131
+ msg: {
14132
+ role: "system",
14133
+ text: `\u26A0\uFE0F MEMORY WARNING: CHAT IS GETTING VERY LONG`,
14134
+ subText: `This session has reached ${slicedCompleted.length} blocks. To maintain optimal performance and prevent high memory usage, it is highly recommended to save and start a clean chat with /clear.`,
14135
+ isHomeWarning: true
14136
+ },
14137
+ type: "full-message"
14138
+ });
14139
+ }
13930
14140
  return {
13931
14141
  completed: slicedCompleted,
13932
14142
  active
@@ -14459,9 +14669,9 @@ function App({ args = [] }) {
14459
14669
  { cmd: "/quit", desc: "Exit and shutdown Flux" },
14460
14670
  { cmd: "/help", desc: "Show all available commands" },
14461
14671
  ...parsedArgs.playground ? [{ cmd: "/move", desc: "Move playground directory to original CWD/playground-export" }] : [],
14672
+ { cmd: "/resume", desc: "Load previous session" },
14462
14673
  { cmd: "/compress", desc: "Summarize and compress chat history" },
14463
14674
  { cmd: "/clear", desc: "Clear terminal screen" },
14464
- { cmd: "/resume", desc: "Load previous session" },
14465
14675
  { cmd: "/revert", desc: "Revert codebase back to a checkpoint" },
14466
14676
  { cmd: "/gemini", desc: "Get a happy message from Gemini CLI" },
14467
14677
  { cmd: "/save", desc: "Force save current chat" },
@@ -15691,20 +15901,29 @@ ${timestamp}` };
15691
15901
  },
15692
15902
  onAskUser: async (question, options) => {
15693
15903
  return new Promise((resolve) => {
15904
+ let resolvedFlag = false;
15694
15905
  setPendingAsk({
15695
15906
  question,
15696
15907
  options,
15697
15908
  resolve: (val) => {
15698
- setMessages((prev) => [
15699
- ...prev,
15700
- {
15701
- id: "ask-" + Date.now(),
15702
- role: "system",
15703
- text: `\u{1F4AC} **Ask User**
15909
+ if (resolvedFlag) return;
15910
+ resolvedFlag = true;
15911
+ setMessages((prev) => {
15912
+ const hasAskRecord = prev.some((m) => m.isAskRecord && m.text?.includes(`Selection: ${val}`));
15913
+ if (hasAskRecord) return prev;
15914
+ const newMsgs = [
15915
+ ...prev,
15916
+ {
15917
+ id: "ask-" + Date.now(),
15918
+ role: "system",
15919
+ text: `\u{1F4AC} **Ask User**
15704
15920
  Selection: ${val}`,
15705
- isAskRecord: true
15706
- }
15707
- ]);
15921
+ isAskRecord: true
15922
+ }
15923
+ ];
15924
+ setCompletedIndex(newMsgs.length);
15925
+ return newMsgs;
15926
+ });
15708
15927
  resolve(val);
15709
15928
  }
15710
15929
  });
@@ -17350,7 +17569,7 @@ var init_app = __esm({
17350
17569
  // src/cli.jsx
17351
17570
  import { spawn as spawn3 } from "child_process";
17352
17571
  import { fileURLToPath as fileURLToPath2 } from "url";
17353
- var HEAP_LIMIT = 4096;
17572
+ var HEAP_LIMIT = 6144;
17354
17573
  var isBundled = fileURLToPath2(import.meta.url).endsWith(".js");
17355
17574
  if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-size"))) {
17356
17575
  const cp = spawn3(process.execPath, [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "2.13.1",
4
- "date": "2026-06-26",
3
+ "version": "2.13.3",
4
+ "date": "2026-06-27",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",
@@ -37,11 +37,12 @@
37
37
  },
38
38
  "scripts": {
39
39
  "start": "tsx ./src/cli.jsx",
40
- "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:@google/genai --external:zod --external:nanoid --external:puppeteer --external:pdf-lib --external:node-pty --external:html-to-docx --external:typescript --external:ws --external:web-tree-sitter"
40
+ "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:@google/genai --external:zod --external:nanoid --external:puppeteer --external:pdf-lib --external:node-pty --external:html-to-docx --external:typescript --external:ws --external:web-tree-sitter --external:diff"
41
41
  },
42
42
  "dependencies": {
43
43
  "@google/genai": "^1.52.0",
44
44
  "chalk": "^5.6.2",
45
+ "diff": "^9.0.0",
45
46
  "fs-extra": "^11.3.4",
46
47
  "gradient-string": "^3.0.0",
47
48
  "html-to-docx": "^1.8.0",