fluxflow-cli 2.8.10 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/fluxflow.js +345 -37
  2. package/package.json +2 -2
package/dist/fluxflow.js CHANGED
@@ -1586,7 +1586,7 @@ var init_ChatLayout = __esm({
1586
1586
  }
1587
1587
  }
1588
1588
  }
1589
- return result.replace(/\[SYSTEM\][\s\S]*?\[\/SYSTEM\]/gi, "").replace(/\[TOOL RESULT\]:?\s*/gi, "").split("\n").filter((line) => !line.trim().startsWith("SUCCESS:") && !line.trim().startsWith("ERROR:")).join("\n").replace(/\[\s*turn\s*:\s*(continue|finish)\s*\]/gi, "").replace(/\[\[END\]\]/gi, "").replace(/\[\s*turn\s*:?.*?$/gi, "").replace(/\n\s*turn\s*:?.*?$/gi, "").replace(/\[\s*$/gi, "").replace(/\n\nResponded on .*/g, "").replace(/\n\n\[Prompted on: .*\]/g, "").replace(/(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi, "\u2192").replace(/(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)/gi, "\u2190").replace(/(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)/gi, "\u2191").replace(/(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)/gi, "\u2193").replace(/(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi, "\u2194").replace(/@\[TerminalName:.*?, ProcessId:.*?\]/gi, "").replace(/\b(write_file|update_file|read_folder|view_file|exec_command|web_search|web_scrape|search_keyword|write_pdf|write_docx|generate_image)\b/gi, (match) => TOOL_LABELS[match.toLowerCase()] || match).trim();
1589
+ return result.replaceAll(/\[SYSTEM\][\s\S]*?\[\/SYSTEM\]/gi, "").replaceAll(/<(think|thought)>[\s\S]*?(?:<\/(think|thought)>|$)/gi, "").replace(/\[ANSWER\][\s\S]*?(?:\[\/ANSWER\]|$)/gi, "").replaceAll(/\[TOOL RESULT\]:?\s*/gi, "").split("\n").filter((line) => !line.trim().startsWith("SUCCESS:") && !line.trim().startsWith("ERROR:")).join("\n").replaceAll(/\[\s*turn\s*:\s*(continue|finish)\s*\]/gi, "").replaceAll(/\[\[END\]\]/gi, "").replaceAll(/\[\s*turn\s*:?.*?$/gi, "").replaceAll(/\n\s*turn\s*:?.*?$/gi, "").replaceAll(/\[\s*$/gi, "").replaceAll(/\n\nResponded on .*/g, "").replaceAll(/\n\n\[Prompted on: .*\]/g, "").replaceAll(/(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi, "\u2192").replaceAll(/(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)/gi, "\u2190").replaceAll(/(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)/gi, "\u2191").replaceAll(/(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)/gi, "\u2193").replaceAll(/(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi, "\u2194").replaceAll(/@\[TerminalName:.*?, ProcessId:.*?\]/gi, "").replaceAll(/\b(write_file|update_file|read_folder|view_file|exec_command|web_search|web_scrape|search_keyword|write_pdf|write_docx|generate_image)\b/gi, (match) => TOOL_LABELS[match.toLowerCase()] || match).trim();
1590
1590
  };
1591
1591
  formatThinkText = (cleaned, columns = 80) => {
1592
1592
  if (!cleaned) return null;
@@ -1852,7 +1852,7 @@ var init_ChatLayout = __esm({
1852
1852
  const isTerminalRecord = msg.isTerminalRecord;
1853
1853
  const isHomeWarning = msg.isHomeWarning;
1854
1854
  if (isHomeWarning) {
1855
- return /* @__PURE__ */ React3.createElement(Box3, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "red", padding: 0, width: "100%" }, /* @__PURE__ */ React3.createElement(Box3, { paddingX: 1, backgroundColor: "#3a0000" }, /* @__PURE__ */ React3.createElement(Text3, { color: "red", bold: true }, msg.text)), /* @__PURE__ */ React3.createElement(Box3, { paddingX: 1, marginTop: 0, marginBottom: 0 }, /* @__PURE__ */ React3.createElement(Text3, { color: "white" }, msg.subText))));
1855
+ return /* @__PURE__ */ React3.createElement(Box3, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", dimColor: true, padding: 0, width: "100%" }, /* @__PURE__ */ React3.createElement(Box3, { paddingX: 1, backgroundColor: "#3a0000" }, /* @__PURE__ */ React3.createElement(Text3, { color: "white", bold: true }, msg.text)), /* @__PURE__ */ React3.createElement(Box3, { paddingX: 1, marginTop: 0, marginBottom: 0 }, /* @__PURE__ */ React3.createElement(Text3, { color: "white" }, msg.subText))));
1856
1856
  }
1857
1857
  if (msg.isLogo) {
1858
1858
  return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", alignItems: "flex-start", width: "100%", marginY: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, getFluxLogo(version, aiProvider)));
@@ -1896,6 +1896,7 @@ var init_ChatLayout = __esm({
1896
1896
  { cmd: "/save", desc: "Force save current chat" },
1897
1897
  { cmd: "/export", desc: "Export current chat in a .txt file" },
1898
1898
  { cmd: "/chats", desc: "List all chat sessions" },
1899
+ { cmd: "/btw", desc: "Send raw inquiry mid-turn" },
1899
1900
  { cmd: "/image", desc: "Generate images" },
1900
1901
  { cmd: "/mode", desc: "Toggle Flux/Flow modes" },
1901
1902
  { cmd: "/thinking", desc: "Set AI reasoning depth" },
@@ -2223,7 +2224,7 @@ ${mode === "Flux" ? "- **File Tools >> Code in chat**\n\n" : ""}- COMMUNICATION
2223
2224
  2. [tool:functions.WebScrape(url="...")]. Proactive use for specific webpage/docs/api
2224
2225
 
2225
2226
  ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative to CWD & WILL BE FIRST ARGUMENT, path separator: '/') -
2226
- 1. [tool:functions.ReadFile(path="...", startLine=number, endLine=number)]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs. User gives image/doc: VIEW FIRST` : `No Multimodal support`}` : `Supports images/docs. User gives image/doc: VIEW FIRST`}
2227
+ 1. [tool:functions.ReadFile(path="...", startLine=number, endLine=number)]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs. **User gives image/doc: VIEW FIRST**` : `No Multimodal support`}` : `Supports images/docs. **User gives image/doc: VIEW FIRST**`}
2227
2228
  2. [tool:functions.ReadFolder(path="...")]. Detailed DIR stats including File Sizes
2228
2229
  3. [tool:functions.FileMap(path="path/file")]. Shows file structure, dependency, functions, variable maps
2229
2230
  4. [tool:functions.PatchFile(path="...", replaceContent1="full line/block", newContent1="...", ...MAX 6)]. Surgical Patch. **Multiple patch on same file/path? Use replaceContent2, newContent2 etc >>> multiple spams**. Unsure? ReadFile >> guessing. **MUST VERIFY DIFF**
@@ -2462,6 +2463,7 @@ var init_exec_command = __esm({
2462
2463
  const looksLikePath = (str) => {
2463
2464
  if (!str.includes("/")) return false;
2464
2465
  if (/^(https?|file|ftp):\/\//i.test(str)) return false;
2466
+ if (str.startsWith("@")) return false;
2465
2467
  if (str.startsWith("/") && (str.match(/\//g) || []).length === 1) {
2466
2468
  return false;
2467
2469
  }
@@ -3448,8 +3450,7 @@ ${projectContextBlock}
3448
3450
 
3449
3451
  -- FORMATTING --
3450
3452
  - GFM Supported
3451
- - NO CHAT RESPONSE **AFTER** CALLING TOOLS IN SAME TURN
3452
- - ONE THINKING BLOCK PER TURN
3453
+ - NO CHAT **AFTER** FIRING TOOLS IN THIS TURN
3453
3454
  - Basic LaTeX${mode === "Flux" ? "" : ". Kaomojis"}
3454
3455
  === END SYSTEM PROMPT ===`.trim();
3455
3456
  };
@@ -6370,6 +6371,7 @@ var init_ai = __esm({
6370
6371
  await init_tools();
6371
6372
  init_crypto();
6372
6373
  init_arg_parser();
6374
+ init_view_file();
6373
6375
  init_terminal();
6374
6376
  init_text();
6375
6377
  init_paths();
@@ -8028,12 +8030,133 @@ ${ideCtx.warnings}
8028
8030
  }
8029
8031
  }
8030
8032
  const cleanAgentText = agentText.replace(/\s*\[Prompted on:.*?\]/g, "").trim();
8033
+ const tagRegex = /@\[([^\]]+)\]/g;
8034
+ let match;
8035
+ const tagsFound = [];
8036
+ tagRegex.lastIndex = 0;
8037
+ while ((match = tagRegex.exec(cleanAgentText)) !== null) {
8038
+ tagsFound.push(match[1]);
8039
+ }
8040
+ let taggedContextBlocks = [];
8041
+ let attachedBinaryPart = null;
8042
+ for (const tag of tagsFound) {
8043
+ try {
8044
+ let tagClean = tag.trim().replace(/^["']|["']$/g, "");
8045
+ const lineRangeRegex = /[:#]L?(\d+)(?:-L?(\d+))?$/i;
8046
+ const matchRange = tagClean.match(lineRangeRegex);
8047
+ let filePath = tagClean;
8048
+ let startLine = null;
8049
+ let endLine = null;
8050
+ if (matchRange) {
8051
+ startLine = parseInt(matchRange[1], 10);
8052
+ endLine = matchRange[2] ? parseInt(matchRange[2], 10) : startLine;
8053
+ filePath = tagClean.slice(0, matchRange.index);
8054
+ }
8055
+ const absPath = path19.resolve(process.cwd(), filePath);
8056
+ if (fs20.existsSync(absPath)) {
8057
+ const stats = fs20.statSync(absPath);
8058
+ if (stats.isFile()) {
8059
+ const pathLower = filePath.toLowerCase();
8060
+ const isPdf = pathLower.endsWith(".pdf");
8061
+ const isOfficeFile = pathLower.endsWith(".docx") || pathLower.endsWith(".doc") || pathLower.endsWith(".ppt") || pathLower.endsWith(".pptx") || pathLower.endsWith(".xls") || pathLower.endsWith(".xlsx");
8062
+ const isImage = /\.(png|jpg|jpeg|webp|gif|bmp)$/.test(pathLower);
8063
+ const isMultimodalFile = isImage || isPdf || isOfficeFile;
8064
+ const isSupported = aiProvider === "Google" || MULTIMODAL_MODELS.includes(modelName);
8065
+ if (isMultimodalFile && !isSupported) {
8066
+ let terminalWidth = 115;
8067
+ if (process.stdout.isTTY) {
8068
+ terminalWidth = process.stdout.columns - 10 || 120;
8069
+ }
8070
+ const boxLines = [`${isImage ? "\u{1F4F8}" : "\u{1F4C4}"} UNSUPPORTED MODALITY FOR THIS MODEL`];
8071
+ const maxLen = Math.max(...boxLines.map((l) => l.length));
8072
+ const boxWidth = Math.min(maxLen + 4, terminalWidth);
8073
+ const boxTop = `\u256D${"\u2500".repeat(boxWidth)}\u256E`;
8074
+ const boxMid = boxLines.map((line) => `\u2502 ${line.padEnd(boxWidth - 2).substring(0, boxWidth - 2)} \u2502`).join("\n");
8075
+ const boxBottom = `\u2570${"\u2500".repeat(boxWidth)}\u256F`;
8076
+ yield { type: "visual_feedback", content: `${boxTop}
8077
+ ${boxMid}
8078
+ ${boxBottom}` };
8079
+ continue;
8080
+ }
8081
+ const finalStart = startLine !== null ? startLine : 1;
8082
+ let finalEnd = endLine !== null ? endLine : startLine !== null ? startLine : finalStart + 499;
8083
+ if (finalEnd - finalStart > 500) {
8084
+ finalEnd = finalStart + 500;
8085
+ }
8086
+ const argsStr = `path=${JSON.stringify(filePath)}, startLine=${finalStart}, endLine=${finalEnd}`;
8087
+ const result = await view_file(argsStr, { isMultiModal: isSupported });
8088
+ let isError = false;
8089
+ let textResult = "";
8090
+ let binPart = null;
8091
+ if (typeof result === "string") {
8092
+ if (result.trim().startsWith("ERROR")) {
8093
+ isError = true;
8094
+ } else {
8095
+ textResult = result;
8096
+ }
8097
+ } else if (result && typeof result === "object") {
8098
+ if (result.binaryPart) {
8099
+ binPart = result.binaryPart;
8100
+ textResult = result.text || "";
8101
+ } else {
8102
+ isError = true;
8103
+ }
8104
+ } else {
8105
+ isError = true;
8106
+ }
8107
+ if (!isError) {
8108
+ let label = "";
8109
+ if (isImage) {
8110
+ label = `\u{1F4F8} Viewed: ${filePath}`;
8111
+ attachedBinaryPart = binPart;
8112
+ } else if (isPdf || isOfficeFile) {
8113
+ label = `\u{1F4C4} Viewed: ${filePath}`;
8114
+ attachedBinaryPart = binPart;
8115
+ } else {
8116
+ let totalLines = "...";
8117
+ try {
8118
+ const content = fs20.readFileSync(absPath, "utf8");
8119
+ totalLines = content.split("\n").length;
8120
+ } catch (e) {
8121
+ }
8122
+ label = `\u{1F4C4} Read: ${filePath} \u2192 Lines ${finalStart} - ${Math.min(finalEnd, totalLines)} of ${totalLines}`;
8123
+ taggedContextBlocks.push(textResult);
8124
+ }
8125
+ if (label) {
8126
+ let terminalWidth = 115;
8127
+ if (process.stdout.isTTY) {
8128
+ terminalWidth = process.stdout.columns - 10 || 120;
8129
+ }
8130
+ const boxLines = [label];
8131
+ const maxLen = Math.max(...boxLines.map((l) => l.length));
8132
+ const boxWidth = Math.min(maxLen + 4, terminalWidth);
8133
+ const boxTop = `\u256D${"\u2500".repeat(boxWidth)}\u256E`;
8134
+ const boxMid = boxLines.map((line) => `\u2502 ${line.padEnd(boxWidth - 2).substring(0, boxWidth - 2)} \u2502`).join("\n");
8135
+ const boxBottom = `\u2570${"\u2500".repeat(boxWidth)}\u256F`;
8136
+ yield { type: "visual_feedback", content: `${boxTop}
8137
+ ${boxMid}
8138
+ ${boxBottom}` };
8139
+ }
8140
+ }
8141
+ }
8142
+ }
8143
+ } catch (e) {
8144
+ }
8145
+ }
8146
+ let taggedContextStr = "";
8147
+ if (taggedContextBlocks.length > 0) {
8148
+ taggedContextStr = "[TAGGED CONTEXT]\n" + taggedContextBlocks.join("\n\n") + "\n[/TAGGED CONTEXT]\n";
8149
+ }
8031
8150
  const firstUserMsg = `[SYSTEM METADATA (PRIORITY: DYNAMIC), Chat Context >> Metadata] Time: ${dateTimeStr}
8032
8151
  CWD: ${process.cwd()}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${lastCwd})` : ""}
8033
8152
  **DIRECTORY STRUCTURE**
8034
8153
  ${dirStructure}${memoryPrompt}${ideBlock}
8035
- ${activeSummaryBlock}${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" && aiProvider === "Google" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[USER] ${cleanAgentText.trim()} [/USER]`.trim();
8036
- modifiedHistory.push({ role: "user", text: firstUserMsg });
8154
+ ${activeSummaryBlock}${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" && aiProvider === "Google" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}${taggedContextStr}[USER] ${cleanAgentText.trim()} [/USER]`.trim();
8155
+ const userMsgObj = { role: "user", text: firstUserMsg };
8156
+ if (attachedBinaryPart) {
8157
+ userMsgObj.binaryPart = attachedBinaryPart;
8158
+ }
8159
+ modifiedHistory.push(userMsgObj);
8037
8160
  if (activeSummaryBlock && history[history.length - 1]?.id) {
8038
8161
  yield { type: "summary_injected", content: { id: history[history.length - 1].id, text: firstUserMsg } };
8039
8162
  }
@@ -8065,14 +8188,25 @@ ${activeSummaryBlock}${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" &&
8065
8188
  if (steeringCallback) {
8066
8189
  const hint = await steeringCallback();
8067
8190
  if (hint) {
8068
- if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "user") {
8069
- modifiedHistory[modifiedHistory.length - 1].text += `
8191
+ if (hint.startsWith("/btw")) {
8192
+ if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "user") {
8193
+ modifiedHistory[modifiedHistory.length - 1].text += `
8070
8194
 
8071
- [STEERING HINT]: ${hint}`;
8195
+ [SYSTEM] USER QUESTION. RESOLVE THIS SPECIFIC QUERY WITHIN '[ANSWER] ... [/ANSWER]' CONCISELY, NATURALLY [/SYSTEM]
8196
+ [QUESTION] ${hint.replace("/btw", "").trim()} [/QUESTION]`;
8197
+ } else {
8198
+ modifiedHistory.push({ role: "user", text: `${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" && aiProvider === "Google" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] USER QUESTION. RESOLVE THIS SPECIFIC QUERY WITHIN '[ANSWER] ... [/ANSWER]' CONCISELY, NATURALLY\n**STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[QUESTION] ${hint.replace("/btw", "").trim()} [/QUESTION]` });
8199
+ }
8072
8200
  } else {
8073
- modifiedHistory.push({ role: "user", text: `${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" && aiProvider === "Google" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[STEERING HINT]: ${hint}` });
8201
+ if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "user") {
8202
+ modifiedHistory[modifiedHistory.length - 1].text += `
8203
+
8204
+ [STEERING HINT] ${hint.trim()} [/STEERING HINT]`;
8205
+ } else {
8206
+ modifiedHistory.push({ role: "user", text: `${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" && aiProvider === "Google" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[STEERING HINT] ${hint.trim()} [/STEERING HINT]` });
8207
+ }
8074
8208
  }
8075
- yield { type: "status", content: "Steering Hint Injected." };
8209
+ yield { type: "status", content: `${hint.startsWith("/btw") ? "Question Forwarded..." : "Steering Hint Injected..."}` };
8076
8210
  }
8077
8211
  }
8078
8212
  let stream;
@@ -8198,13 +8332,15 @@ ${activeSummaryBlock}${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" &&
8198
8332
  currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? "GEM" : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, isMultiModal);
8199
8333
  const lastUserMsg = contents[contents.length - 1];
8200
8334
  if (isBridgeConnected()) {
8201
- await new Promise((resolve) => setTimeout(resolve, 2e3));
8335
+ yield { type: "status", content: "Checking Code..." };
8336
+ await new Promise((resolve) => setTimeout(resolve, 2500));
8202
8337
  const ideCtxJIT = await getIDEContext();
8203
8338
  const ideErr = ideCtxJIT ? ideCtxJIT.diagnostics : null;
8204
8339
  if (ideErr && lastUserMsg && lastUserMsg.role === "user" && lastUserMsg.parts?.[0]?.text) {
8205
8340
  lastUserMsg.parts[0].text += `
8206
8341
  ${ideErr} [/ERROR]`;
8207
8342
  }
8343
+ yield { type: "status", content: "Working..." };
8208
8344
  }
8209
8345
  const isGemma = modelName && modelName.toLowerCase().startsWith("gemma") && aiProvider === "Google";
8210
8346
  if (isGemma) {
@@ -8353,14 +8489,14 @@ ${ideErr} [/ERROR]`;
8353
8489
  { type: "end", idx: endIdx, start: "[[END]]", end: "[[END]]" }
8354
8490
  ].filter((i) => i.idx !== -1).sort((a, b) => a.idx - b.idx);
8355
8491
  if (indices.length > 0) {
8356
- const match = indices[0];
8357
- if (match.idx > 0) {
8358
- msgs.push({ type: "text", content: remaining.substring(0, match.idx) });
8492
+ const match2 = indices[0];
8493
+ if (match2.idx > 0) {
8494
+ msgs.push({ type: "text", content: remaining.substring(0, match2.idx) });
8359
8495
  }
8360
8496
  isBufferingToolCall = true;
8361
- activeBufferType = match.type;
8497
+ activeBufferType = match2.type;
8362
8498
  toolCallBuffer = "";
8363
- remaining = remaining.substring(match.idx);
8499
+ remaining = remaining.substring(match2.idx);
8364
8500
  } else {
8365
8501
  const potentialStarts = ["[tool", "[[END]]"];
8366
8502
  let splitPoint = -1;
@@ -8763,12 +8899,112 @@ ${ideErr} [/ERROR]`;
8763
8899
  if (command && settings.systemSettings && settings.systemSettings.allowExternalAccess === false) {
8764
8900
  const riskyPatterns = [/[a-zA-Z]:[\\\/]/i, /^\//, /\.\.[\\\/]/, /\/etc\//, /\/var\//, /\/root\//, /\/bin\//, /\/usr\//];
8765
8901
  const currentDrive = path19.resolve(process.cwd()).substring(0, 3).toLowerCase();
8766
- const isViolating = riskyPatterns.some((pattern) => {
8767
- if (pattern.source === "[a-zA-Z]:[\\\\\\/]") {
8768
- const driveMatch = command.match(/[a-zA-Z]:[\\\/]/i);
8769
- return driveMatch && driveMatch[0].toLowerCase() !== currentDrive;
8902
+ const splitCommands = (cmdString) => {
8903
+ const commands = [];
8904
+ let current = "";
8905
+ let inQuote = null;
8906
+ for (let i = 0; i < cmdString.length; i++) {
8907
+ const char = cmdString[i];
8908
+ if (inQuote) {
8909
+ if (char === inQuote) inQuote = null;
8910
+ current += char;
8911
+ } else {
8912
+ if (char === '"' || char === "'") {
8913
+ inQuote = char;
8914
+ current += char;
8915
+ } else if (char === "&" && cmdString[i + 1] === "&" || char === "|" && cmdString[i + 1] === "|") {
8916
+ if (current.trim()) {
8917
+ commands.push(current.trim());
8918
+ current = "";
8919
+ }
8920
+ i++;
8921
+ } else if (char === ";" || char === "|" || char === "&") {
8922
+ if (current.trim()) {
8923
+ commands.push(current.trim());
8924
+ current = "";
8925
+ }
8926
+ } else {
8927
+ current += char;
8928
+ }
8929
+ }
8770
8930
  }
8771
- return pattern.test(command);
8931
+ if (current.trim()) {
8932
+ commands.push(current.trim());
8933
+ }
8934
+ return commands;
8935
+ };
8936
+ const tokenizeCommand = (cmd) => {
8937
+ const tokens = [];
8938
+ let current = "";
8939
+ let inQuote = null;
8940
+ for (let i = 0; i < cmd.length; i++) {
8941
+ const char = cmd[i];
8942
+ if (inQuote) {
8943
+ if (char === inQuote) {
8944
+ inQuote = null;
8945
+ current += char;
8946
+ } else {
8947
+ current += char;
8948
+ }
8949
+ } else {
8950
+ if (char === '"' || char === "'") {
8951
+ inQuote = char;
8952
+ current += char;
8953
+ } else if (/\s/.test(char)) {
8954
+ if (current) {
8955
+ tokens.push(current);
8956
+ current = "";
8957
+ }
8958
+ } else {
8959
+ current += char;
8960
+ }
8961
+ }
8962
+ }
8963
+ if (current) {
8964
+ tokens.push(current);
8965
+ }
8966
+ return tokens;
8967
+ };
8968
+ const checkToken = (token) => {
8969
+ const cleanToken = token.replace(/^['"]|['"]$/g, "").trim();
8970
+ if (!cleanToken) return false;
8971
+ if (process.platform === "win32" && /^\/[a-zA-Z0-9?]+$/.test(cleanToken)) {
8972
+ return false;
8973
+ }
8974
+ return riskyPatterns.some((pattern) => {
8975
+ if (pattern.source === "[a-zA-Z]:[\\\\\\/]") {
8976
+ const driveMatch = cleanToken.match(/[a-zA-Z]:[\\\/]/i);
8977
+ return driveMatch && driveMatch[0].toLowerCase() !== currentDrive;
8978
+ }
8979
+ return pattern.test(cleanToken);
8980
+ });
8981
+ };
8982
+ const commandParts = splitCommands(command);
8983
+ const isViolating = commandParts.some((cmdPart) => {
8984
+ const tokens = tokenizeCommand(cmdPart);
8985
+ if (tokens.length === 0) return false;
8986
+ const exe = tokens[0].replace(/^['"]|['"]$/g, "").toLowerCase();
8987
+ const isSafePrint = ["echo", "printf", "write-output"].includes(exe);
8988
+ if (isSafePrint) {
8989
+ let checkNext = false;
8990
+ return tokens.some((token) => {
8991
+ const clean = token.replace(/^['"]|['"]$/g, "");
8992
+ if (clean === ">" || clean === ">>" || clean === "<") {
8993
+ checkNext = true;
8994
+ return false;
8995
+ }
8996
+ if (clean.startsWith(">") || clean.startsWith("<")) {
8997
+ const pathPart = clean.replace(/^[><]+/, "");
8998
+ return checkToken(pathPart);
8999
+ }
9000
+ if (checkNext) {
9001
+ checkNext = false;
9002
+ return checkToken(token);
9003
+ }
9004
+ return false;
9005
+ });
9006
+ }
9007
+ return tokens.some((token) => checkToken(token));
8772
9008
  });
8773
9009
  if (isViolating) {
8774
9010
  const denyMsg = `Access Denied. Terminal is prohibited from accessing system drives (C://) or external directories while "External Workspace Access" is disabled.`;
@@ -10919,6 +11155,28 @@ function App({ args = [] }) {
10919
11155
  return msgs;
10920
11156
  });
10921
11157
  const queuedPromptRef = useRef3(null);
11158
+ const [btwResponse, setBtwResponse] = useState11("");
11159
+ const [showBtwBox, setShowBtwBox] = useState11(false);
11160
+ const btwResponseRef = useRef3("");
11161
+ const btwClosedRef = useRef3(null);
11162
+ useEffect8(() => {
11163
+ if (messages.length === 0) return;
11164
+ const lastMsg = messages[messages.length - 1];
11165
+ if (lastMsg && (lastMsg.role === "agent" || lastMsg.role === "assistant")) {
11166
+ const text = lastMsg.text || "";
11167
+ const match = text.match(/\[ANSWER\]([\s\S]*?)(?:\[\/ANSWER\]|$)/i);
11168
+ if (match) {
11169
+ const content = match[1].trim();
11170
+ if (content && content !== btwResponseRef.current) {
11171
+ setBtwResponse(content);
11172
+ btwResponseRef.current = content;
11173
+ if (btwClosedRef.current !== lastMsg.id) {
11174
+ setShowBtwBox(true);
11175
+ }
11176
+ }
11177
+ }
11178
+ }
11179
+ }, [messages]);
10922
11180
  const [completedIndex, setCompletedIndex] = useState11(messages.length);
10923
11181
  const windowedHistory = useMemo2(() => {
10924
11182
  const MAX_HISTORY_LINES = 2e3;
@@ -11029,6 +11287,16 @@ function App({ args = [] }) {
11029
11287
  return;
11030
11288
  }
11031
11289
  if (key.escape) {
11290
+ if (showBtwBox) {
11291
+ setShowBtwBox(false);
11292
+ if (messages.length > 0) {
11293
+ const lastMsg = messages[messages.length - 1];
11294
+ if (lastMsg) {
11295
+ btwClosedRef.current = lastMsg.id;
11296
+ }
11297
+ }
11298
+ return;
11299
+ }
11032
11300
  if (suggestions.length > 0 && activeView === "chat") {
11033
11301
  setIsFilePickerDismissed(true);
11034
11302
  return;
@@ -11415,6 +11683,7 @@ function App({ args = [] }) {
11415
11683
  { cmd: "/save", desc: "Force save current chat" },
11416
11684
  { cmd: "/export", desc: "Export current chat in a .txt file" },
11417
11685
  { cmd: "/chats", desc: "List all chat sessions" },
11686
+ { cmd: "/btw", desc: "Ask a question without intefering with ongoing tasks" },
11418
11687
  // {
11419
11688
  // cmd: '/image', desc: 'Generate images using Pollinations', subs: [
11420
11689
  // {
@@ -11711,7 +11980,17 @@ function App({ args = [] }) {
11711
11980
  if (!absoluteClean.trim()) return;
11712
11981
  if (isProcessing) {
11713
11982
  const hintText = absoluteClean.trim();
11714
- if (hintText.startsWith("/")) {
11983
+ if (hintText.startsWith("/btw")) {
11984
+ const question = hintText.replace(/^\/btw\s*/, "").trim();
11985
+ if (question.length <= 3) {
11986
+ setMessages((prev) => {
11987
+ setCompletedIndex(prev.length + 1);
11988
+ return [...prev, { id: "hint-err-" + Date.now(), role: "system", text: "[RESTRICTED] Inquiry question must be more than 3 characters.", isMeta: true }];
11989
+ });
11990
+ setInput("");
11991
+ return;
11992
+ }
11993
+ } else if (hintText.startsWith("/")) {
11715
11994
  setMessages((prev) => {
11716
11995
  setCompletedIndex(prev.length + 1);
11717
11996
  return [...prev, { id: "hint-err-" + Date.now(), role: "system", text: "[RESTRICTED] Steering Hints cannot start with /", isMeta: true }];
@@ -11723,8 +12002,11 @@ function App({ args = [] }) {
11723
12002
  queuedPromptRef.current = hintText;
11724
12003
  setMessages((prev) => {
11725
12004
  setCompletedIndex(prev.length + 1);
11726
- return [...prev, { id: "hint-" + Date.now(), role: "user", text: `[STEERING HINT: QUEUED]
11727
- ${hintText}`, color: "magenta" }];
12005
+ const isBtw = hintText.startsWith("/btw");
12006
+ const cleanText = isBtw ? hintText.replace(/^\/btw\s*/, "") : hintText;
12007
+ const prefix = isBtw ? "[QUESTION: QUEUED]" : "[STEERING HINT: QUEUED]";
12008
+ return [...prev, { id: "hint-" + Date.now(), role: "user", text: `${prefix}
12009
+ ${cleanText}`, color: "magenta" }];
11728
12010
  });
11729
12011
  setInput("");
11730
12012
  return;
@@ -12497,13 +12779,19 @@ Selection: ${val}`,
12497
12779
  setQueuedPrompt(null);
12498
12780
  queuedPromptRef.current = null;
12499
12781
  setMessages((prev) => {
12500
- const index = [...prev].reverse().findIndex((m) => m.text?.includes("[STEERING HINT: QUEUED]"));
12782
+ const index = [...prev].reverse().findIndex((m) => m.text?.includes("[STEERING HINT: QUEUED]") || m.text?.includes("[QUESTION: QUEUED]"));
12501
12783
  if (index !== -1) {
12502
12784
  const actualIndex = prev.length - 1 - index;
12503
12785
  const newMsgs = [...prev];
12786
+ let text = newMsgs[actualIndex].text;
12787
+ if (text.includes("[STEERING HINT: QUEUED]")) {
12788
+ text = text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: INJECTED]");
12789
+ } else if (text.includes("[QUESTION: QUEUED]")) {
12790
+ text = text.replace("[QUESTION: QUEUED]", "[QUESTION: ASKED]");
12791
+ }
12504
12792
  newMsgs[actualIndex] = {
12505
12793
  ...newMsgs[actualIndex],
12506
- text: newMsgs[actualIndex].text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: INJECTED]"),
12794
+ text,
12507
12795
  color: "cyan"
12508
12796
  };
12509
12797
  return newMsgs;
@@ -12594,7 +12882,7 @@ Selection: ${val}`,
12594
12882
  }
12595
12883
  if (packet.type === "visual_feedback") {
12596
12884
  setMessages((prev) => [...prev, {
12597
- id: "feedback-" + Date.now(),
12885
+ id: "feedback-" + Date.now() + "-" + Math.random().toString(36).substring(2, 9),
12598
12886
  role: "system",
12599
12887
  text: packet.content,
12600
12888
  isVisualFeedback: true
@@ -12721,11 +13009,26 @@ Selection: ${val}`,
12721
13009
  const hasThinkTag = chunkLower.includes("<think") || chunkLower.includes("<thought");
12722
13010
  const canThink = !inThinkMode && !inCodeBlock && !inToolCall && !thinkConsumedInTurn;
12723
13011
  if (hasThinkTag && canThink) {
13012
+ const match = chunkText.match(/<(think|thought)/i);
13013
+ const tagIndex = match.index;
13014
+ const beforeText = chunkText.substring(0, tagIndex);
13015
+ const afterText = chunkText.substring(tagIndex);
13016
+ if (beforeText) {
13017
+ if (!currentAgentId) {
13018
+ currentAgentId = "agent-" + Date.now();
13019
+ setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: beforeText, isStreaming: true }]);
13020
+ } else {
13021
+ setMessages((prev) => prev.map(
13022
+ (m) => m.id === currentAgentId ? { ...m, text: m.text + beforeText, isStreaming: true } : m
13023
+ ));
13024
+ }
13025
+ }
12724
13026
  inThinkMode = true;
12725
13027
  thinkConsumedInTurn = true;
12726
- chunkText = chunkText.replace(/<(think|thought)>[\s\S]*?<\/(think|thought)>/gi, "").replace(/<(think|thought)>/gi, "");
13028
+ let thinkStartText = afterText.replace(/<(think|thought)>/gi, "");
12727
13029
  currentThinkId = "think-" + Date.now();
12728
- setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: "", isStreaming: true, startTime: Date.now() }]);
13030
+ setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: thinkStartText, isStreaming: true, startTime: Date.now() }]);
13031
+ continue;
12729
13032
  }
12730
13033
  if ((chunkLower.includes("</think>") || chunkLower.includes("</thought>")) && currentThinkId) {
12731
13034
  const parts = chunkText.split(/<\/(think|thought)>/gi);
@@ -12810,9 +13113,13 @@ Selection: ${val}`,
12810
13113
  queuedPromptRef.current = null;
12811
13114
  setMessages((prev) => {
12812
13115
  const newMsgs = [...prev];
12813
- const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]"));
13116
+ const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]") || m.text?.includes("[QUESTION: QUEUED]"));
12814
13117
  if (hintMsg) {
12815
- hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: FINISHED_TURN]");
13118
+ if (hintMsg.text.includes("[STEERING HINT: QUEUED]")) {
13119
+ hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: FINISHED_TURN]");
13120
+ } else if (hintMsg.text.includes("[QUESTION: QUEUED]")) {
13121
+ hintMsg.text = hintMsg.text.replace("[QUESTION: QUEUED]", "[QUESTION: FINISHED_TURN]");
13122
+ }
12816
13123
  }
12817
13124
  return newMsgs.reverse();
12818
13125
  });
@@ -13295,7 +13602,7 @@ Selection: ${val}`,
13295
13602
  }
13296
13603
  ));
13297
13604
  case "keybindingsPrompt":
13298
- return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React14.createElement(Text14, { color: "cyan", bold: true, underline: true }, "\u2328\uFE0F CONFIGURE SHIFT+ENTER NEWLINE"), /* @__PURE__ */ React14.createElement(Text14, { marginTop: 1 }, "To support multi-line inputs with ", /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "white" }, "Shift + Enter"), " for newline, a terminal sequence keybinding needs to be added to your IDE configuration."), /* @__PURE__ */ React14.createElement(Text14, { marginTop: 1 }, "Would you like FluxFlow to automatically add this to your ", getIDEName(), " keybindings?"), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(
13605
+ return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "grey", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React14.createElement(Text14, { color: "white", bold: true, underline: true }, "\u2328 CONFIGURE SHIFT+ENTER NEWLINE"), /* @__PURE__ */ React14.createElement(Text14, { marginTop: 1 }, "To support multi-line inputs with ", /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "white" }, "Shift + Enter"), " for newline, a terminal sequence keybinding needs to be added to your IDE configuration."), /* @__PURE__ */ React14.createElement(Text14, { marginTop: 1 }, "Would you like FluxFlow to automatically add this to your ", getIDEName(), " keybindings?"), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(
13299
13606
  CommandMenu,
13300
13607
  {
13301
13608
  title: "Add Keybinding?",
@@ -13482,7 +13789,7 @@ Selection: ${val}`,
13482
13789
  }
13483
13790
  )));
13484
13791
  default:
13485
- return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1, marginBottom: 0, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React14.createElement(Box14, null, statusText ? /* @__PURE__ */ React14.createElement(Box14, null, /* @__PURE__ */ React14.createElement(Text14, { color: "gray", bold: true, italic: true }, statusText)) : /* @__PURE__ */ React14.createElement(Text14, { color: "gray", italic: true }, input.length > 0 && escPressCount ? "Press ESC again to clear input" : "Waiting for input...")), /* @__PURE__ */ React14.createElement(Box14, null, wittyPhrase && /* @__PURE__ */ React14.createElement(Text14, { color: "gray", italic: true }, wittyPhrase, " "), /* @__PURE__ */ React14.createElement(Text14, { color: "gray", bold: true }, "[ "), /* @__PURE__ */ React14.createElement(Text14, { color: "white" }, tempModelOverride || activeModel), /* @__PURE__ */ React14.createElement(Text14, { color: "gray", bold: true }, " ]"))), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React14.createElement(Box14, { width: "100%", height: 1, overflow: "hidden" }, /* @__PURE__ */ React14.createElement(Text14, { color: "#555555" }, "\u2584".repeat(Math.max(1, terminalSize.columns)))), /* @__PURE__ */ React14.createElement(
13792
+ return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, showBtwBox && btwResponse && /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "grey", paddingX: 2, paddingY: 1, width: "100%", marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Box14, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React14.createElement(Text14, { color: "white", bold: true, underline: true }, "INQUIRY RESPONSE"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, "[ ESC to Close ]")), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React14.createElement(CodeRenderer, { text: btwResponse, columns: terminalSize.columns - 6 }))), /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1, marginBottom: 0, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React14.createElement(Box14, null, statusText ? /* @__PURE__ */ React14.createElement(Box14, null, /* @__PURE__ */ React14.createElement(Text14, { color: "gray", bold: true, italic: true }, statusText)) : /* @__PURE__ */ React14.createElement(Text14, { color: "gray", italic: true }, input.length > 0 && escPressCount ? "Press ESC again to clear input" : "Waiting for input...")), /* @__PURE__ */ React14.createElement(Box14, null, wittyPhrase && /* @__PURE__ */ React14.createElement(Text14, { color: "gray", italic: true }, wittyPhrase, " "), /* @__PURE__ */ React14.createElement(Text14, { color: "gray", bold: true }, "[ "), /* @__PURE__ */ React14.createElement(Text14, { color: "white" }, tempModelOverride || activeModel), /* @__PURE__ */ React14.createElement(Text14, { color: "gray", bold: true }, " ]"))), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React14.createElement(Box14, { width: "100%", height: 1, overflow: "hidden" }, /* @__PURE__ */ React14.createElement(Text14, { color: "#555555" }, "\u2584".repeat(Math.max(1, terminalSize.columns)))), /* @__PURE__ */ React14.createElement(
13486
13793
  Box14,
13487
13794
  {
13488
13795
  backgroundColor: "#555555",
@@ -13758,7 +14065,7 @@ var init_app = __esm({
13758
14065
  height
13759
14066
  },
13760
14067
  /* @__PURE__ */ React14.createElement(Box14, { marginBottom: 1, width: Math.min(80, width - 4), justifyContent: "flex-start" }, /* @__PURE__ */ React14.createElement(Text14, null, getFluxLogo(versionFluxflow))),
13761
- /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "double", borderColor: "grey", paddingX: 3, paddingY: 1, width: Math.min(80, width - 4) }, /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "white", textAlign: "center" }, "\u{1F680} UPGRADE YOUR WORKFLOW"), /* @__PURE__ */ React14.createElement(Box14, { marginY: 1, flexDirection: "column", alignItems: "left" }, /* @__PURE__ */ React14.createElement(Text14, null, "You're in ", /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "cyan" }, ideName), ", but the ", /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "white" }, "FluxFlow-CLI Companion"), " is not installed."), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Real-time file & cursor tracking"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Auto-open files created by agent"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Native DIFF viewer for AI edits"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Direct IDE context sharing"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Surgical Diagnostic Sync"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Native Right-Click \u276F Chat integration"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Live Status in IDE"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Clickable terminal-to-code links"))), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", marginTop: 1 }, options.map((opt, i) => /* @__PURE__ */ React14.createElement(Box14, { key: i }, /* @__PURE__ */ React14.createElement(Text14, { color: selectedIndex === i ? "yellow" : "white", bold: selectedIndex === i }, selectedIndex === i ? " \u276F " : " ", opt.label)))), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 1, alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React14.createElement(Text14, { dimColor: true, italic: true }, "(Use arrows to navigate, Enter to select)")))
14068
+ /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "double", borderColor: "grey", paddingX: 3, paddingY: 1, width: Math.min(80, width - 4) }, /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "white", textAlign: "center" }, "\u{1F680} UPGRADE YOUR WORKFLOW"), /* @__PURE__ */ React14.createElement(Box14, { marginY: 1, flexDirection: "column", alignItems: "left" }, /* @__PURE__ */ React14.createElement(Text14, null, "You're in ", /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "cyan" }, ideName), ", but the ", /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "white" }, "FluxFlow-CLI Companion"), " is not installed."), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Real-time IDE context & Error Resolution"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Auto-open files created by agent"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Native DIFFing for AI edits"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Direct IDE context sharing"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Surgical Diagnostic Sync"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Native Right-Click \u276F Chat integration"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Live Status in IDE"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " \u2705 Clickable terminal-to-code links"))), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", marginTop: 1 }, options.map((opt, i) => /* @__PURE__ */ React14.createElement(Box14, { key: i }, /* @__PURE__ */ React14.createElement(Text14, { color: selectedIndex === i ? "yellow" : "white", bold: selectedIndex === i }, selectedIndex === i ? " \u276F " : " ", opt.label)))), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 1, alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React14.createElement(Text14, { dimColor: true, italic: true }, "(Use arrows to navigate, Enter to select)")))
13762
14069
  );
13763
14070
  };
13764
14071
  SESSION_START_TIME = Date.now();
@@ -13770,7 +14077,7 @@ var init_app = __esm({
13770
14077
  packageJson = JSON.parse(fs22.readFileSync(packageJsonPath, "utf8"));
13771
14078
  versionFluxflow = packageJson.version;
13772
14079
  updatedOn = packageJson.date || "2026-05-20";
13773
- ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1 }, /* @__PURE__ */ React14.createElement(Text14, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION")), /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text14, null, "The agent already finished the task before your hint was consumed.")), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 1, backgroundColor: "#222", paddingX: 2, width: "100%" }, /* @__PURE__ */ React14.createElement(Text14, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text14, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 0 }, /* @__PURE__ */ React14.createElement(
14080
+ ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "grey", padding: 0, width: "100%" }, /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1 }, /* @__PURE__ */ React14.createElement(Text14, { color: "white", bold: true, underline: true }, data.startsWith("/btw") ? "QUESTION" : "STEERING HINT", " RESOLUTION")), /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text14, null, "The agent already finished the task before your ", data.startsWith("/btw") ? "question" : "hint", " was consumed.")), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 1, backgroundColor: "#222", paddingX: 2, width: "100%" }, /* @__PURE__ */ React14.createElement(Text14, { italic: true, color: "gray" }, '"', data.replace("/btw", "").trim(), '"')), /* @__PURE__ */ React14.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text14, { color: "grey" }, "How would you like to proceed?")), /* @__PURE__ */ React14.createElement(Box14, { marginTop: 0 }, /* @__PURE__ */ React14.createElement(
13774
14081
  CommandMenu,
13775
14082
  {
13776
14083
  title: "Select Action",
@@ -13954,6 +14261,7 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
13954
14261
  /save Force save current chat
13955
14262
  /export Export current chat in a .txt file
13956
14263
  /chats List all chat sessions
14264
+ /btw <question> Send raw inquiry to the agent mid-turn
13957
14265
  /image setup key <default|custom> Configure image API key strategy
13958
14266
  /image setup quality <low...premium> Configure default image generation quality
13959
14267
  /image stats Show image quota stats
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "2.8.10",
4
- "date": "2026-06-19",
3
+ "version": "2.10.0",
4
+ "date": "2026-06-20",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",