fluxflow-cli 1.16.5 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/fluxflow.js +175 -37
  2. package/package.json +2 -2
package/dist/fluxflow.js CHANGED
@@ -238,7 +238,7 @@ var init_ChatLayout = __esm({
238
238
  };
239
239
  InlineMarkdown = React2.memo(({ text, color }) => {
240
240
  if (!text) return null;
241
- const parts = text.split(/(```[\s\S]*?```|`[^`]+`|\*\*.*?\*\*|\*.*?\*|\$.*?\$|\[.*?\]\s*\(.*?\)|\[.*?\]\s*\[.*?\]|https?:\/\/[^\s]+)/g);
241
+ const parts = text.split(/(```[\s\S]*?```|`[^`]+`|@\[.*?\]|\*\*.*?\*\*|\*.*?\*|\$.*?\$|\[.*?\]\s*\(.*?\)|\[.*?\]\s*\[.*?\]|https?:\/\/[^\s]+)/g);
242
242
  return /* @__PURE__ */ React2.createElement(Text2, { color, wrap: "anywhere" }, parts.map((part, j) => {
243
243
  if (!part) return null;
244
244
  if (part.startsWith("```") && part.endsWith("```")) {
@@ -253,6 +253,11 @@ var init_ChatLayout = __esm({
253
253
  if (part.startsWith("`") && part.endsWith("`")) {
254
254
  return /* @__PURE__ */ React2.createElement(Text2, { key: j, color: "cyan", backgroundColor: "#003333" }, " ", part.slice(1, -1), " ");
255
255
  }
256
+ if (part.startsWith("@[") && part.endsWith("]")) {
257
+ const filePath = part.slice(2, -1);
258
+ const basename = filePath.split("/").pop().split("\\").pop();
259
+ return /* @__PURE__ */ React2.createElement(Text2, { key: j, color: "cyan", backgroundColor: "#1a1a2e" }, " ", basename, " ");
260
+ }
256
261
  if (part.startsWith("$") && part.endsWith("$")) {
257
262
  const content = part.slice(1, -1);
258
263
  const latexParts = content.split(/(\\(?:mathbf|textbf|textit|underline|text|mathrm|textsf|texttt)\{.*?\})/g);
@@ -529,8 +534,8 @@ var init_ChatLayout = __esm({
529
534
  wrapText(
530
535
  finalContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\n/g, "\n").replace(/\\$/, ""),
531
536
  columns - 6
532
- ).split("\n").map((line, lineIdx) => /* @__PURE__ */ React2.createElement(Box2, { key: lineIdx, flexDirection: "row", width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexShrink: 0, width: 2 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, lineIdx === 0 ? "\u276F" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: msg.color || "white", wrap: "anywhere" }, line))))
533
- ) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 0, marginBottom: 0, paddingX: 1, width: "100%" }, msg.isStreaming && !msg.duration ? /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "\u2727 Thinking...") : /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "\u2726 Thought", msg.duration ? /* @__PURE__ */ React2.createElement(Text2, { dimColor: true, color: "gray" }, " for ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, formatThinkingDuration(msg.duration))) : ""), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, paddingTop: 1, paddingBottom: 1, flexDirection: "column", width: "100%" }, formatThinkText(finalContent, columns))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: finalContent.replace(/ \|\n\n/g, " |\n"), columns }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))))
537
+ ).split("\n").map((line, lineIdx) => /* @__PURE__ */ React2.createElement(Box2, { key: lineIdx, flexDirection: "row", width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexShrink: 0, width: 2 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, lineIdx === 0 ? "\u276F" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(InlineMarkdown, { text: line, color: msg.color || "white" }))))
538
+ ) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 0, marginBottom: 0, paddingX: 1, width: "100%" }, msg.isStreaming && !msg.duration ? /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "\u2727 Thinking...") : /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "\u2726 Thought", msg.duration ? /* @__PURE__ */ React2.createElement(Text2, { dimColor: true, color: "gray" }, " for ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, formatThinkingDuration(msg.duration))) : ""), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, paddingTop: 1, paddingBottom: 1, flexDirection: "column", width: "100%" }, formatThinkText(finalContent, columns))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: finalContent.replace(/ \|\n\n/g, " |\n"), columns }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]")), msg.role === "agent" && msg.workedDuration ? /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true, color: "gray" }, "[\u26A1 Worked for ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, formatThinkingDuration(msg.workedDuration)), " ]")) : null))
534
539
  );
535
540
  });
536
541
  ChatLayout = React2.memo(({ messages, showFullThinking, columns = 80 }) => {
@@ -1275,7 +1280,7 @@ var init_main_tools = __esm({
1275
1280
  TOOL_PROTOCOL = (mode, osDetected) => `
1276
1281
  -- TOOL DEFINITIONS --
1277
1282
  Access to internal tools. To call a tool, MUST use the exact syntax on a new line: [tool:functions.ToolName(args)]
1278
- - **STRICT POLICY: MAX 3 TOOL CALLS PER TURN. Next Turn, verify results and plan next**
1283
+ - **STRICT POLICY: MAX 4 TOOL CALLS PER TURN. Next Turn, verify results, plan next**
1279
1284
 
1280
1285
  - COMMUNICATION TOOLS -
1281
1286
  1. [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Ambiguity Resolution. Mandatory Triggers: Path Divergence, Security, Risk Mitigation. ask >> finish
@@ -1288,8 +1293,8 @@ Suggest best options; don't ask for preferences
1288
1293
  ${mode === "Flux" ? `- PROJECT TOOLS (path = relative to CWD) -
1289
1294
  1. [tool:functions.ReadFile(path="...", startLine=number, endLine=number)]. Supports images/docs. User gives image/doc: VIEW FIRST
1290
1295
  2. [tool:functions.ReadFolder(path="...")]. Detailed DIR stats
1291
- 3. [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. Verify Imports
1292
- 4. [tool:functions.PatchFile(path="...", replaceContent="exact old content", newContent="new content")]. Surgical patching. Unsure replaceContent? ReadFile > guessing
1296
+ 3. [tool:functions.PatchFile(path="...", replaceContent="exact old content", newContent="new content")]. Surgical patching. Unsure replaceContent? ReadFile > guessing
1297
+ 4. [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile >> WriteFile. Verify Imports
1293
1298
  5. [tool:functions.SearchKeyword(keyword="...")]. Global project search. Finds definitions/logic without reading every file
1294
1299
  6. [tool:functions.Run(command="...")]. Runs a ${osDetected === "Windows" ? "Windows CMD" : "Bash"} command. Destructive/Irreversible ops -> Ask user
1295
1300
  7. [tool:functions.GenerateImage(path="... png", prompt="detailed", ratio="16:9, 9:16, 1:1")]. Usage: Mockups, PDF thumbnails, any visual content
@@ -1297,12 +1302,12 @@ ${mode === "Flux" ? `- PROJECT TOOLS (path = relative to CWD) -
1297
1302
  9. [tool:functions.WriteDoc(path="...", content="...")]. A4 Word document
1298
1303
 
1299
1304
  - VERIFY TOOL RESULT CONTENTS. Fix errors. No hallucinations
1300
- - File tools > Long chat
1305
+ - File tools >>> Long chat
1301
1306
 
1302
1307
  - Escape quotes: \\" for code strings
1303
1308
  - Literal escapes: Double-escape sequences (e.g., \\\\n, \\\\t)
1304
1309
  - File structure: Real newlines for code formatting`.trim() : `
1305
- - FILE TOOLS ARE NOT AVAILABLE IN FLOW`.trim()}
1310
+ - FILE TOOLS ARE NOT AVAILABLE IN FLOW (Tell user,\` /mode flux\` if needed)`.trim()}
1306
1311
 
1307
1312
  - Results: Passed as [TOOL RESULT] (system priority)
1308
1313
  - MAX Tool call stack: STRICTLY 3 per turn`.trim();
@@ -1366,7 +1371,7 @@ var init_prompts = __esm({
1366
1371
  init_thinking_prompts();
1367
1372
  getMemoryPrompt = (tempMemories = "", userMemories = "", isMemoryEnabled = true, isContext32k = false) => {
1368
1373
  if (!isMemoryEnabled) return "";
1369
- const tempMemoriesStr = tempMemories?.length > 0 && !isContext32k ? `-- RECENT CONTEXT FROM OTHER CHATS (PRIORITY: DYNAMIC-MEDIUM, FOCUS: Chat Context > Recent) --
1374
+ const tempMemoriesStr = tempMemories?.length > 0 && !isContext32k ? `-- RECENT CONTEXT FROM OTHER CHATS (PRIORITY: DYNAMIC-LOW, FOCUS: Chat Context > Recent) --
1370
1375
  ${tempMemories}` : "";
1371
1376
  const userMemoriesStr = userMemories?.length > 0 ? `--- SAVED MEMORIES (PRIORITY: MEDIUM, USER PREFERENCES) ---
1372
1377
  ${userMemories}` : "";
@@ -1419,8 +1424,7 @@ Check these first; These Files > Training Data. Safety rules apply
1419
1424
  return `${nameStr}${nicknameStr}${userInstrStr}[SYSTEM]
1420
1425
  Identity: Flux Flow (by Kushal Roy Chowdhury). Sassy${mode === "Flux" ? ", No Flirting, Respectful" : ", Friendly, Humorous, Sarcastic"}, CLI Agent
1421
1426
  Mode: ${mode}${thinkingLevel !== "Fast" ? " (Thinking Mode)" : ""}. ${mode === "Flux" ? "Logical, Highly Detailed, Task-Driven. Prioritizes scalable file/folder structures, modular architecture, clean code abstractions, step-by-step execution. Industry standard latest coding practices/libraries, clean code, Double Check Imports, Client-Server Sync" : "Conversational, Concise"}
1422
- CWD: ${cwdStr}${isSystemDir ? ". [PROTECTED: ASK BEFORE MODIFYING]" : ""}
1423
-
1427
+ ${isSystemDir ? "[PROTECTED DIRECTORY: ASK BEFORE MODIFYING]\n" : ""}
1424
1428
  -- THINKING RULES --
1425
1429
  ${thinkingConfig}
1426
1430
  ${thinkingLevel !== "Fast" ? `
@@ -4636,8 +4640,71 @@ ${newMemoryListStr}
4636
4640
  const isContext32k = (sessionStats?.tokens || 0) >= 32e3;
4637
4641
  const memoryPrompt = getMemoryPrompt(otherMemories, mainUserMemories, isMemoryEnabled, isContext32k);
4638
4642
  const dateTimeStr = (/* @__PURE__ */ new Date()).toLocaleString([], { year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit", hour12: true });
4639
- const firstUserMsg = `${memoryPrompt}
4640
- [METADATA (PRIORITY: DYNAMIC)] Time: ${dateTimeStr} | v${versionFluxflow2}
4643
+ const getDirTree = (dir, prefix = "") => {
4644
+ try {
4645
+ const files = fs16.readdirSync(dir);
4646
+ const sep = path15.sep;
4647
+ if (files.length > 70) {
4648
+ return `${prefix}\u2514\u2500\u2500 ${path15.basename(dir)}${sep}...
4649
+ `;
4650
+ }
4651
+ let result = "";
4652
+ const COLLAPSED_DIRS = [".git", "node_modules", ".gemini", "dist", "build", ".next", "out", ".cache", "bin", "obj", "vendor"];
4653
+ const filtered = files.filter((f) => !COLLAPSED_DIRS.includes(f));
4654
+ const collapsedInDir = files.filter((f) => COLLAPSED_DIRS.includes(f)).sort();
4655
+ const sorted = filtered.sort((a, b) => {
4656
+ try {
4657
+ const aStat = fs16.statSync(path15.join(dir, a));
4658
+ const bStat = fs16.statSync(path15.join(dir, b));
4659
+ if (aStat.isDirectory() && !bStat.isDirectory()) return -1;
4660
+ if (!aStat.isDirectory() && bStat.isDirectory()) return 1;
4661
+ } catch (e) {
4662
+ }
4663
+ return a.localeCompare(b);
4664
+ });
4665
+ sorted.push(...collapsedInDir);
4666
+ sorted.forEach((file, index) => {
4667
+ const isLast = index === sorted.length - 1;
4668
+ const filePath = path15.join(dir, file);
4669
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
4670
+ const childPrefix = prefix + (isLast ? " " : "\u2502 ");
4671
+ if (COLLAPSED_DIRS.includes(file)) {
4672
+ result += `${prefix}${connector}${file}${sep}...
4673
+ `;
4674
+ return;
4675
+ }
4676
+ try {
4677
+ const stat = fs16.statSync(filePath);
4678
+ if (stat.isDirectory()) {
4679
+ const subFiles = fs16.readdirSync(filePath);
4680
+ if (subFiles.length > 80) {
4681
+ result += `${prefix}${connector}${file}${sep}...
4682
+ `;
4683
+ } else {
4684
+ result += `${prefix}${connector}${file}${sep}
4685
+ `;
4686
+ result += getDirTree(filePath, childPrefix);
4687
+ }
4688
+ } else {
4689
+ result += `${prefix}${connector}${file}
4690
+ `;
4691
+ }
4692
+ } catch (e) {
4693
+ result += `${prefix}${connector}${file}
4694
+ `;
4695
+ }
4696
+ });
4697
+ return result;
4698
+ } catch (e) {
4699
+ return "";
4700
+ }
4701
+ };
4702
+ let dirStructure = process.cwd() + "\n" + getDirTree(process.cwd());
4703
+ const firstUserMsg = `[METADATA (PRIORITY: DYNAMIC)] Time: ${dateTimeStr} | v${versionFluxflow2}
4704
+ CWD: ${process.cwd()}
4705
+ **DIRECTORY STRUCTURE**
4706
+ ${dirStructure}
4707
+ ${memoryPrompt}
4641
4708
  ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CORE PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>**\n" : ""}[USER] ${agentText.replace(/\s*\[Prompted on:.*?\]/g, "").trim()}`.trim();
4642
4709
  modifiedHistory.push({ role: "user", text: firstUserMsg });
4643
4710
  let lastUsage = null;
@@ -5222,7 +5289,7 @@ ${boxBottom}` };
5222
5289
  await new Promise((resolve) => setTimeout(resolve, 1e3 - timeSinceLastTool));
5223
5290
  }
5224
5291
  }
5225
- const effectiveStart = lastToolEventTime || Date.now();
5292
+ const executionStart = Date.now();
5226
5293
  yield { type: "spinner", content: false };
5227
5294
  let result = await dispatchTool(normToolName, toolCall.args, {
5228
5295
  chatId,
@@ -5237,7 +5304,7 @@ ${boxBottom}` };
5237
5304
  }
5238
5305
  const toolEnd = Date.now();
5239
5306
  lastToolFinishedAt = toolEnd;
5240
- yield { type: "tool_time", content: toolEnd - effectiveStart };
5307
+ yield { type: "tool_time", content: toolEnd - executionStart };
5241
5308
  lastToolEventTime = toolEnd;
5242
5309
  let binaryPart = null;
5243
5310
  if (typeof result === "object" && result.binaryPart) {
@@ -5858,6 +5925,7 @@ function App({ args = [] }) {
5858
5925
  rows: stdout?.rows || 24
5859
5926
  });
5860
5927
  const [selectedIndex, setSelectedIndex] = useState9(0);
5928
+ const [isFilePickerDismissed, setIsFilePickerDismissed] = useState9(false);
5861
5929
  const persistedModelRef = useRef2(null);
5862
5930
  const parsedArgs = useMemo(() => {
5863
5931
  const parsed = {};
@@ -6153,6 +6221,10 @@ function App({ args = [] }) {
6153
6221
  }
6154
6222
  }
6155
6223
  if (key.escape) {
6224
+ if (suggestions.length > 0 && activeView === "chat") {
6225
+ setIsFilePickerDismissed(true);
6226
+ return;
6227
+ }
6156
6228
  if (confirmExit) {
6157
6229
  setConfirmExit(false);
6158
6230
  return;
@@ -7454,7 +7526,16 @@ Selection: ${val}`,
7454
7526
  setActiveView("resolution");
7455
7527
  }
7456
7528
  setMessages((prev) => {
7457
- const newMsgs = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
7529
+ const totalDuration = Date.now() - apiStart;
7530
+ let foundLastAgent = false;
7531
+ const newMsgs = [...prev].reverse().map((m) => {
7532
+ let updated = m.isStreaming ? { ...m, isStreaming: false } : m;
7533
+ if (!foundLastAgent && updated.role === "agent") {
7534
+ foundLastAgent = true;
7535
+ updated = { ...updated, workedDuration: totalDuration };
7536
+ }
7537
+ return updated;
7538
+ }).reverse();
7458
7539
  const historyToSave = newMsgs.filter((m) => !String(m.id).startsWith("welcome") && !m.isMeta);
7459
7540
  saveChat(chatId, null, historyToSave);
7460
7541
  setCompletedIndex(newMsgs.length);
@@ -7468,28 +7549,48 @@ Selection: ${val}`,
7468
7549
  setIsExpanded(false);
7469
7550
  };
7470
7551
  const suggestions = useMemo(() => {
7471
- if (!input.startsWith("/")) return [];
7552
+ if (input.startsWith("/") && !isFilePickerDismissed) {
7553
+ const parts2 = input.split(" ");
7554
+ const query = parts2[parts2.length - 1].toLowerCase();
7555
+ if (parts2.length === 1) {
7556
+ const cleanQuery = query.startsWith("/") ? query.slice(1) : query;
7557
+ return COMMANDS.filter((c) => {
7558
+ const cleanCmd = c.cmd.startsWith("/") ? c.cmd.slice(1) : c.cmd;
7559
+ return cleanCmd.toLowerCase().includes(cleanQuery);
7560
+ });
7561
+ }
7562
+ let currentList = COMMANDS;
7563
+ for (let i = 0; i < parts2.length - 1; i++) {
7564
+ const part = parts2[i].toLowerCase();
7565
+ const found = currentList.find((c) => c.cmd.toLowerCase() === part);
7566
+ if (found && found.subs) {
7567
+ currentList = found.subs;
7568
+ } else {
7569
+ return [];
7570
+ }
7571
+ }
7572
+ return currentList.filter((s) => s.cmd.toLowerCase().includes(query));
7573
+ }
7472
7574
  const parts = input.split(" ");
7473
- const query = parts[parts.length - 1].toLowerCase();
7474
- if (parts.length === 1) {
7475
- const cleanQuery = query.startsWith("/") ? query.slice(1) : query;
7476
- return COMMANDS.filter((c) => {
7477
- const cleanCmd = c.cmd.startsWith("/") ? c.cmd.slice(1) : c.cmd;
7478
- return cleanCmd.toLowerCase().includes(cleanQuery);
7575
+ const lastPart = parts[parts.length - 1];
7576
+ if (lastPart && lastPart.startsWith("@") && !isFilePickerDismissed) {
7577
+ const hashIndex = lastPart.indexOf("#");
7578
+ const hasHash = hashIndex !== -1;
7579
+ const query = hasHash ? lastPart.substring(1, hashIndex).toLowerCase() : lastPart.slice(1).toLowerCase();
7580
+ const suffix = hasHash ? lastPart.substring(hashIndex) : "";
7581
+ const projectFiles = getProjectFiles(process.cwd());
7582
+ const matches = projectFiles.filter((f) => f.name.toLowerCase().includes(query));
7583
+ return matches.map((f) => {
7584
+ const relPath = f.relativePath.replace(/\\/g, "/");
7585
+ const formattedPath = relPath.startsWith(".") ? relPath : "./" + relPath;
7586
+ return {
7587
+ cmd: "@[" + formattedPath + suffix + "]",
7588
+ desc: f.relativePath
7589
+ };
7479
7590
  });
7480
7591
  }
7481
- let currentList = COMMANDS;
7482
- for (let i = 0; i < parts.length - 1; i++) {
7483
- const part = parts[i].toLowerCase();
7484
- const found = currentList.find((c) => c.cmd.toLowerCase() === part);
7485
- if (found && found.subs) {
7486
- currentList = found.subs;
7487
- } else {
7488
- return [];
7489
- }
7490
- }
7491
- return currentList.filter((s) => s.cmd.toLowerCase().includes(query));
7492
- }, [input]);
7592
+ return [];
7593
+ }, [input, isFilePickerDismissed]);
7493
7594
  useEffect6(() => {
7494
7595
  setSelectedIndex(0);
7495
7596
  }, [suggestions]);
@@ -7979,6 +8080,7 @@ Selection: ${val}`,
7979
8080
  onChange: (val) => {
7980
8081
  const cleanVal = val.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\s*\n/g, "\n");
7981
8082
  setInput(cleanVal);
8083
+ setIsFilePickerDismissed(false);
7982
8084
  },
7983
8085
  onSubmit: handleSubmit,
7984
8086
  maxRows: 3,
@@ -8039,7 +8141,7 @@ Selection: ${val}`,
8039
8141
  paddingY: 0,
8040
8142
  width: "100%"
8041
8143
  },
8042
- /* @__PURE__ */ React12.createElement(Box12, { paddingX: 1, marginBottom: 0 }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray", bold: true, dimColor: true }, "\u{1F50D} COMMAND SUGGESTIONS")),
8144
+ /* @__PURE__ */ React12.createElement(Box12, { paddingX: 1, marginBottom: 0, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React12.createElement(Text12, { color: "gray", bold: true, dimColor: true }, suggestions[0]?.cmd?.startsWith("@") ? "\u{1F4C1} FILE SUGGESTIONS" : "\u{1F50D} COMMAND SUGGESTIONS"), suggestions[0]?.cmd?.startsWith("@") && /* @__PURE__ */ React12.createElement(Text12, { color: "gray", dimColor: true, italic: true }, "(Use '#Lstart-Lend' to specify line numbers)")),
8043
8145
  visible.map((s, i) => {
8044
8146
  const actualIdx = startIdx + i;
8045
8147
  const isActive = actualIdx === selectedIndex;
@@ -8069,7 +8171,7 @@ Selection: ${val}`,
8069
8171
  );
8070
8172
  })()));
8071
8173
  }
8072
- var SESSION_START_TIME, CHANGELOG_URL, linesAdded, linesRemoved, packageJsonPath, packageJson, versionFluxflow, updatedOn, ResolutionModal, FLUX_LOGO, parseAgentText;
8174
+ var SESSION_START_TIME, CHANGELOG_URL, linesAdded, linesRemoved, packageJsonPath, packageJson, versionFluxflow, updatedOn, ResolutionModal, FLUX_LOGO, parseAgentText, getProjectFiles;
8073
8175
  var init_app = __esm({
8074
8176
  "src/app.jsx"() {
8075
8177
  init_ChatLayout();
@@ -8190,6 +8292,42 @@ var init_app = __esm({
8190
8292
  }
8191
8293
  return blocks;
8192
8294
  };
8295
+ getProjectFiles = /* @__PURE__ */ (() => {
8296
+ let cachedFiles = null;
8297
+ let lastScanTime = 0;
8298
+ return (dir) => {
8299
+ const now = Date.now();
8300
+ if (cachedFiles && now - lastScanTime < 5e3) {
8301
+ return cachedFiles;
8302
+ }
8303
+ const fileList = [];
8304
+ const scan = (currentDir) => {
8305
+ try {
8306
+ const files = fs18.readdirSync(currentDir);
8307
+ for (const file of files) {
8308
+ if (["node_modules", ".git", ".gemini", "dist", "build", ".next", ".cache", "out"].includes(file)) {
8309
+ continue;
8310
+ }
8311
+ const filePath = path16.join(currentDir, file);
8312
+ const stat = fs18.statSync(filePath);
8313
+ if (stat.isDirectory()) {
8314
+ scan(filePath);
8315
+ } else {
8316
+ fileList.push({
8317
+ name: file,
8318
+ relativePath: path16.relative(process.cwd(), filePath)
8319
+ });
8320
+ }
8321
+ }
8322
+ } catch (e) {
8323
+ }
8324
+ };
8325
+ scan(dir);
8326
+ cachedFiles = fileList;
8327
+ lastScanTime = now;
8328
+ return fileList;
8329
+ };
8330
+ })();
8193
8331
  }
8194
8332
  });
8195
8333
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.16.5",
4
- "date": "2026-05-28",
3
+ "version": "1.17.1",
4
+ "date": "2026-05-29",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",