fluxflow-cli 1.16.3 → 1.17.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 +171 -33
  2. package/package.json +1 -1
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,6 +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)]
1283
+ - **STRICT POLICY: MAX 3 TOOL CALLS PER TURN. Next Turn, verify results, plan next**
1278
1284
 
1279
1285
  - COMMUNICATION TOOLS -
1280
1286
  1. [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Ambiguity Resolution. Mandatory Triggers: Path Divergence, Security, Risk Mitigation. ask >> finish
@@ -1365,7 +1371,7 @@ var init_prompts = __esm({
1365
1371
  init_thinking_prompts();
1366
1372
  getMemoryPrompt = (tempMemories = "", userMemories = "", isMemoryEnabled = true, isContext32k = false) => {
1367
1373
  if (!isMemoryEnabled) return "";
1368
- 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) --
1369
1375
  ${tempMemories}` : "";
1370
1376
  const userMemoriesStr = userMemories?.length > 0 ? `--- SAVED MEMORIES (PRIORITY: MEDIUM, USER PREFERENCES) ---
1371
1377
  ${userMemories}` : "";
@@ -1418,8 +1424,7 @@ Check these first; These Files > Training Data. Safety rules apply
1418
1424
  return `${nameStr}${nicknameStr}${userInstrStr}[SYSTEM]
1419
1425
  Identity: Flux Flow (by Kushal Roy Chowdhury). Sassy${mode === "Flux" ? ", No Flirting, Respectful" : ", Friendly, Humorous, Sarcastic"}, CLI Agent
1420
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"}
1421
- CWD: ${cwdStr}${isSystemDir ? ". [PROTECTED: ASK BEFORE MODIFYING]" : ""}
1422
-
1427
+ ${isSystemDir ? "[PROTECTED DIRECTORY: ASK BEFORE MODIFYING]\n" : ""}
1423
1428
  -- THINKING RULES --
1424
1429
  ${thinkingConfig}
1425
1430
  ${thinkingLevel !== "Fast" ? `
@@ -1444,7 +1449,6 @@ ${projectContextBlock}
1444
1449
  -- RESPONSE RULES --
1445
1450
  - End with [turn: continue] to continue or [turn: finish] when task done
1446
1451
  - Tool Called? No post tool response until [turn: continue]
1447
- - **STRICT: MAX 3 TOOL CALL STACK PER TURN**
1448
1452
  - Task Complete? End loop with [turn: finish]
1449
1453
  [/SYSTEM]`.trim();
1450
1454
  };
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.16.3",
3
+ "version": "1.17.0",
4
4
  "date": "2026-05-28",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [