fluxflow-cli 1.0.7 → 1.0.9

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.
package/ARCHITECTURE.md CHANGED
@@ -25,7 +25,9 @@ The execution flow of a single user prompt follows this loop:
25
25
  5. **Turn Management & Continuation**: The model is instructed to append `[turn: finish]` if its goal is complete, or `[turn: continue]` if it expects tool results.
26
26
  - If tools were called or `[turn: continue]` is present, the loop increments and re-prompts the model with the newly gathered `[TOOL_RESULT]` data.
27
27
  - If `[turn: finish]` is detected and no further tools were called, the main loop terminates, passing the final synthesized context to the background Janitor process.
28
- 6. **Loop Limits & Resilience**: To prevent infinite loops or excessive API usage, **Flux mode** is capped at 50 iterations per user prompt, while **Flow mode** is capped at 5. The loop also features built-in retry logic (with exponential backoff) for handling transient API errors like 429s or 503s.
28
+ 6. **Loop Limits & Resilience**: To prevent infinite loops or excessive API usage, **Flux mode** is capped at 50 iterations per user prompt, while **Flow mode** is capped at 5.
29
+ - **Multi-Stage Failover**: The loop features a sophisticated 8-attempt retry engine with random backoff (800ms - 2s).
30
+ - **Critical Fallback Pivot**: If the primary model fails 5 consecutive times, the agent surgically pivots to a lighter, high-concurrency fallback model (`gemini-3.1-flash-lite-preview`) for the final 3 attempts to ensure session navigation through API congestion.
29
31
 
30
32
  ## The Dual-Model System
31
33
 
package/README.md CHANGED
@@ -40,6 +40,7 @@ Security isn't an afterthought; it's a boundary.
40
40
  - **External Path Hardlock**: Restricts the agent to your Current Working Directory (CWD) unless you explicitly unlock it.
41
41
  - **Human-in-the-Loop (HITL)**: Every file write and terminal command requires your high-fidelity approval.
42
42
  - **XOR Vaulting**: All local session histories, memories, and API keys are obfuscated and encrypted at rest.
43
+ - **Adaptive Failover**: Automatic multi-stage retry logic with high-concurrency fallback model switching (Gemini 3.1 Flash Lite) during peak API congestion.
43
44
 
44
45
  ### 🧹 **The Background Janitor**
45
46
  While you move at high speed, the Janitor follows behind—refining session titles, compressing data, and ensuring your context window remains at absolute peak performance.
@@ -50,6 +51,7 @@ While you move at high speed, the Janitor follows behind—refining session titl
50
51
  - **Deep File-System Interaction**: Edit, move, and refactor code across multiple files with atomic precision.
51
52
  - **Real-Time Web Intelligence**: Autonomous web-searching via DuckDuckGo for live news and technical research.
52
53
  - **Autonomous Project Alignment**: Automatically detects and adheres to project-specific instructions in `Agent.md`, `Skills.md`, and `Fluxflow.md` for high-fidelity alignment with your coding standards and custom workflows.
54
+ - **High-Reliability Fallback**: Automatic failover to a lighter, high-concurrency model during peak traffic to ensure zero session loss.
53
55
  - **Persistent Memory**: The agent learns from your preferences and project requirements across sessions.
54
56
 
55
57
  ---
package/dist/fluxflow.js CHANGED
@@ -12,7 +12,7 @@ import { MultilineInput } from "ink-multiline-input";
12
12
  import TextInput2 from "ink-text-input";
13
13
 
14
14
  // src/components/ChatLayout.jsx
15
- import React2 from "react";
15
+ import React2, { memo } from "react";
16
16
  import { Box as Box2, Text as Text2 } from "ink";
17
17
 
18
18
  // src/components/TerminalBox.jsx
@@ -172,6 +172,19 @@ function ChatLayout({ messages, showFullThinking }) {
172
172
  ) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Thinking..."), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, flexDirection: "column" }, formatThinkText(content))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: content }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))));
173
173
  }));
174
174
  }
175
+ var ChatLayout_default = memo(ChatLayout, (prevProps, nextProps) => {
176
+ if (prevProps.showFullThinking !== nextProps.showFullThinking) return false;
177
+ if (prevProps.messages.length !== nextProps.messages.length) return false;
178
+ for (let i = 0; i < prevProps.messages.length; i++) {
179
+ const prev = prevProps.messages[i];
180
+ const next = nextProps.messages[i];
181
+ if (prev.id !== next.id) return false;
182
+ if (prev.text !== next.text) return false;
183
+ if (prev.memoryUpdated !== next.memoryUpdated) return false;
184
+ if (prev.color !== next.color) return false;
185
+ }
186
+ return true;
187
+ });
175
188
 
176
189
  // src/components/StatusBar.jsx
177
190
  import React3 from "react";
@@ -356,7 +369,7 @@ ${mode === "Flux" ? `
356
369
  Results will be provided in the next loop as: [TOOL_RESULT]: [content]
357
370
  WHEN CALLING TOOLS, YOU **MUST** END YOUR RESPONSE WITH '[turn: continue]' AFTER CALLING FUNCTIONS.
358
371
  Do NOT over-use tools. Use them only when strictly necessary for the user's objective. You can stack multiple tool calls 1-by-1.
359
- ALWAYS USE TOOLS WITH 'tool:' PREFIX AS INSTRUCTED OR THE TOOL. NEVER CASUALLY WRITE TOOL CALLS TO USER FACING RESPONSE. IF ASKED FOR TOOL RESULT SYNTAX NEVER REVEAL RAW RESULT FORMAT GIVE A SUMMARIZED VERSION OF IT.
372
+ Distinguish clearly between tool discussion and execution. Use the 'tool:' prefix ONLY when calling a function. When discussing tools with the user, refer to them by name as nouns (e.g., 'write_file', 'list_files') to avoid accidental triggers and context bloat.
360
373
  -- END FUNCTION CALLING PROTOCOL --`.trim();
361
374
 
362
375
  // src/data/janitor_tools.js
@@ -392,7 +405,7 @@ var thinking_prompts_default = {
392
405
  };
393
406
 
394
407
  // src/utils/prompts.js
395
- var getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMemories = "", userMemories = "", isMemoryEnabled = true) => {
408
+ var getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMemories = "", userMemories = "", isContext50 = false, isMemoryEnabled = true) => {
396
409
  let levelKey = thinkingLevel;
397
410
  if (thinkingLevel === "Low") levelKey = "Minimal";
398
411
  if (thinkingLevel === "xHigh" || thinkingLevel === "Max") levelKey = "Max";
@@ -402,7 +415,7 @@ var getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMe
402
415
  const userInstrStr = profile.instructions && profile.instructions?.length > 0 ? `. User Instructions: ${profile.instructions}.` : "";
403
416
  const dateTimeStr = (/* @__PURE__ */ new Date()).toLocaleString();
404
417
  const cwdStr = process.cwd();
405
- const tempMemoriesStr = tempMemories?.length > 0 ? `
418
+ const tempMemoriesStr = tempMemories?.length > 0 && !isContext50 ? `
406
419
  -- RECENT CONTEXT FROM OTHER CHAT THREADS --
407
420
  ${tempMemories}
408
421
  ------------------------------------------
@@ -548,15 +561,10 @@ var deleteChat = async (id) => {
548
561
  const history = await loadHistory();
549
562
  delete history[id];
550
563
  await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
551
- if (await fs3.pathExists(TEMP_MEM_FILE)) {
552
- try {
553
- const temp = await fs3.readJson(TEMP_MEM_FILE);
554
- if (temp[id]) {
555
- delete temp[id];
556
- await fs3.writeJson(TEMP_MEM_FILE, temp, { spaces: 2 });
557
- }
558
- } catch (e) {
559
- }
564
+ const temp = readEncryptedJson(TEMP_MEM_FILE, {});
565
+ if (temp[id]) {
566
+ delete temp[id];
567
+ writeEncryptedJson(TEMP_MEM_FILE, temp);
560
568
  }
561
569
  return history;
562
570
  });
@@ -1278,14 +1286,15 @@ var getAIStream = async function* (modelName, history, settings, steeringCallbac
1278
1286
  const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
1279
1287
  const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
1280
1288
  const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
1281
- const systemInstruction = getSystemInstruction(profile, thinkingLevel, mode, systemSettings, otherMemories, mainUserMemories, isMemoryEnabled);
1289
+ const isContext50 = (sessionStats.tokens || 0) >= 54e3;
1290
+ const systemInstruction = getSystemInstruction(profile, thinkingLevel, mode, systemSettings, otherMemories, mainUserMemories, isMemoryEnabled, isContext50);
1282
1291
  const firstUserMsg = `${systemInstruction}
1283
1292
 
1284
1293
  USER_PROMPT: ${agentText}`.trim();
1285
1294
  modifiedHistory.push({ role: "user", text: firstUserMsg });
1286
1295
  let lastUsage = null;
1287
- const MAX_LOOPS = mode === "Flux" ? 50 : 5;
1288
- const MAX_RETRIES = 3;
1296
+ const MAX_LOOPS = mode === "Flux" ? 50 : 7;
1297
+ const MAX_RETRIES = 7;
1289
1298
  yield { type: "status", content: "Working..." };
1290
1299
  TERMINATION_SIGNAL = false;
1291
1300
  let fullAgentResponse = "";
@@ -1314,8 +1323,15 @@ USER_PROMPT: ${agentText}`.trim();
1314
1323
  if (!await checkQuota("agent", settings)) {
1315
1324
  throw new Error("Error: Daily Quota Exausted for Agent");
1316
1325
  }
1326
+ let targetModel = modelName;
1327
+ if (retryCount >= 5) {
1328
+ targetModel = "gemini-3.1-flash-lite-preview";
1329
+ yield { type: "model_update", content: "Trying with fallback model" };
1330
+ } else if (retryCount > 0) {
1331
+ yield { type: "model_update", content: null };
1332
+ }
1317
1333
  stream = await client.models.generateContentStream({
1318
- model: modelName,
1334
+ model: targetModel,
1319
1335
  contents,
1320
1336
  config: {
1321
1337
  temperature: mode === "Flux" ? 0.9 : 1.3,
@@ -1326,31 +1342,39 @@ USER_PROMPT: ${agentText}`.trim();
1326
1342
  }
1327
1343
  });
1328
1344
  success = true;
1345
+ yield { type: "model_update", content: null };
1329
1346
  } catch (err) {
1330
- const errMsg = err.message || String(err);
1347
+ const errMsg = err.status || err.error && err.error.message || String(err);
1331
1348
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
1332
1349
  const agentErrDir = path13.join(LOGS_DIR, "agent");
1333
- if (!fs12.existsSync(agentErrDir)) {
1334
- fs12.mkdirSync(agentErrDir, { recursive: true });
1335
- }
1350
+ if (!fs12.existsSync(agentErrDir)) fs12.mkdirSync(agentErrDir, { recursive: true });
1336
1351
  fs12.appendFileSync(path13.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
1337
1352
  `);
1338
- const isRetryable = errMsg.includes("429") || errMsg.includes("503") || errMsg.includes("overloaded") || errMsg.includes("deadline");
1339
- if (isRetryable && retryCount < MAX_RETRIES) {
1353
+ if (retryCount < MAX_RETRIES) {
1340
1354
  retryCount++;
1355
+ const waitTime = Math.floor(Math.random() * (2e3 - 800 + 1)) + 800;
1341
1356
  yield { type: "status", content: `Retrying (${retryCount}/${MAX_RETRIES})...` };
1342
- await new Promise((resolve) => setTimeout(resolve, 2e3 * retryCount));
1357
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
1343
1358
  } else {
1344
1359
  throw new Error(`Model cannot be reached: ${errMsg}`);
1345
1360
  }
1346
1361
  }
1347
1362
  }
1348
1363
  let turnText = "";
1364
+ let lastToolSniffed = null;
1349
1365
  for await (const chunk of stream) {
1350
1366
  if (TERMINATION_SIGNAL) break;
1351
1367
  if (chunk.text) {
1352
1368
  turnText += chunk.text;
1353
1369
  yield { type: "text", content: chunk.text };
1370
+ if (turnText.includes("tool:functions.")) {
1371
+ const parts = turnText.split("tool:functions.");
1372
+ const potentialTool = parts[parts.length - 1].split("(")[0].trim();
1373
+ if (potentialTool && /^[a-z_]+$/.test(potentialTool) && potentialTool !== lastToolSniffed) {
1374
+ lastToolSniffed = potentialTool;
1375
+ yield { type: "status", content: `Working (${potentialTool})...` };
1376
+ }
1377
+ }
1354
1378
  }
1355
1379
  if (chunk.usageMetadata) {
1356
1380
  lastUsage = chunk.usageMetadata;
@@ -1505,11 +1529,16 @@ ${boxBottom}
1505
1529
  }
1506
1530
  const cleanResultForAI = result.split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n");
1507
1531
  toolResults.push(`[TOOL_RESULT]: ${cleanResultForAI}`);
1508
- yield { type: "tool_result", content: `[TOOL_RESULT]: ${result}` };
1532
+ let uiContent = `[TOOL_RESULT]: ${result}`;
1533
+ if (toolCall.toolName === "view_file" || toolCall.toolName === "web_scrape") {
1534
+ uiContent = `[TOOL_RESULT]: ${label} (Context Locked for UI Clarity)`;
1535
+ }
1536
+ yield { type: "tool_result", content: uiContent };
1509
1537
  if (toolCall.toolName === "memory" && result.includes("SUCCESS")) {
1510
1538
  yield { type: "memory_updated" };
1511
1539
  }
1512
1540
  }
1541
+ yield { type: "status", content: "Working..." };
1513
1542
  }
1514
1543
  const cleanedTurnText = turnText.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/\[?\s*(turn\s*:)?\s*(continue|finish)\s*\]?/gi, "").trim();
1515
1544
  if (hasFinish || !shouldContinue && toolResults.length === 0) {
@@ -1708,13 +1737,11 @@ function ResumeModal({ onSelect, onDelete, onClose }) {
1708
1737
  // src/components/MemoryModal.jsx
1709
1738
  import React7, { useState as useState3, useEffect as useEffect2 } from "react";
1710
1739
  import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
1711
- import path15 from "path";
1712
- var MEMORIES_PATH = path15.join(process.cwd(), "secret", "memories.json");
1713
1740
  function MemoryModal({ onClose }) {
1714
1741
  const [memories, setMemories] = useState3([]);
1715
1742
  const [selectedIndex, setSelectedIndex] = useState3(0);
1716
1743
  const loadMemories = () => {
1717
- const data = readEncryptedJson(MEMORIES_PATH, []);
1744
+ const data = readEncryptedJson(MEMORIES_FILE, []);
1718
1745
  setMemories(data);
1719
1746
  };
1720
1747
  useEffect2(() => {
@@ -1727,7 +1754,7 @@ function MemoryModal({ onClose }) {
1727
1754
  if (input === "x" && memories.length > 0) {
1728
1755
  const idToDelete = memories[selectedIndex].id;
1729
1756
  const updated = memories.filter((m) => m.id !== idToDelete);
1730
- writeEncryptedJson(MEMORIES_PATH, updated);
1757
+ writeEncryptedJson(MEMORIES_FILE, updated);
1731
1758
  setMemories(updated);
1732
1759
  if (selectedIndex >= updated.length && updated.length > 0) {
1733
1760
  setSelectedIndex(updated.length - 1);
@@ -1831,9 +1858,11 @@ function App() {
1831
1858
  const [escTimer, setEscTimer] = useState4(null);
1832
1859
  const [queuedPrompt, setQueuedPrompt] = useState4(null);
1833
1860
  const [resolutionData, setResolutionData] = useState4(null);
1861
+ const [tempModelOverride, setTempModelOverride] = useState4(null);
1834
1862
  const [messages, setMessages] = useState4([
1835
1863
  { id: "welcome", role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome to Flux Flow! Type /help for commands.\n" }
1836
1864
  ]);
1865
+ const queuedPromptRef = useRef(null);
1837
1866
  const [completedIndex, setCompletedIndex] = useState4(1);
1838
1867
  useInput3((inputText, key) => {
1839
1868
  if (key.escape) {
@@ -1921,6 +1950,7 @@ function App() {
1921
1950
  if (isProcessing) {
1922
1951
  const hintText = absoluteClean.trim();
1923
1952
  setQueuedPrompt(hintText);
1953
+ queuedPromptRef.current = hintText;
1924
1954
  setMessages((prev) => {
1925
1955
  setCompletedIndex(prev.length + 1);
1926
1956
  return [...prev, { id: "hint-" + Date.now(), role: "user", text: `[STEERING HINT: QUEUED]
@@ -2184,9 +2214,19 @@ OUTPUT: ${execOutputRef.current}`;
2184
2214
  }
2185
2215
  },
2186
2216
  async () => {
2187
- if (queuedPrompt) {
2188
- const p = queuedPrompt;
2217
+ if (queuedPromptRef.current) {
2218
+ const p = queuedPromptRef.current;
2189
2219
  setQueuedPrompt(null);
2220
+ queuedPromptRef.current = null;
2221
+ setMessages((prev) => {
2222
+ const newMsgs = [...prev];
2223
+ const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]"));
2224
+ if (hintMsg) {
2225
+ hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: INJECTED]");
2226
+ hintMsg.color = "cyan";
2227
+ }
2228
+ return newMsgs.reverse();
2229
+ });
2190
2230
  return p;
2191
2231
  }
2192
2232
  return null;
@@ -2201,6 +2241,10 @@ OUTPUT: ${execOutputRef.current}`;
2201
2241
  setStatusText(packet.content);
2202
2242
  continue;
2203
2243
  }
2244
+ if (packet.type === "model_update") {
2245
+ setTempModelOverride(packet.content);
2246
+ continue;
2247
+ }
2204
2248
  if (packet.type === "turn_reset") {
2205
2249
  currentThinkId = null;
2206
2250
  currentAgentId = null;
@@ -2300,9 +2344,19 @@ OUTPUT: ${execOutputRef.current}`;
2300
2344
  } finally {
2301
2345
  setIsProcessing(false);
2302
2346
  setStatusText(null);
2303
- if (queuedPrompt) {
2304
- setResolutionData(queuedPrompt);
2347
+ if (queuedPromptRef.current) {
2348
+ setResolutionData(queuedPromptRef.current);
2305
2349
  setQueuedPrompt(null);
2350
+ const hintToResolve = queuedPromptRef.current;
2351
+ queuedPromptRef.current = null;
2352
+ setMessages((prev) => {
2353
+ const newMsgs = [...prev];
2354
+ const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]"));
2355
+ if (hintMsg) {
2356
+ hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: FINISHED_TURN]");
2357
+ }
2358
+ return newMsgs.reverse();
2359
+ });
2306
2360
  setActiveView("resolution");
2307
2361
  }
2308
2362
  setMessages((prev) => {
@@ -2371,7 +2425,7 @@ OUTPUT: ${execOutputRef.current}`;
2371
2425
  CommandMenu,
2372
2426
  {
2373
2427
  title: "\u{1F916} Select AI Model",
2374
- items: [{ label: "Gemma 4 31B (Default)", value: "gemma-4-31b-it" }, { label: "Gemini 3.1 Pro (Req. paid API Key)", value: "gemini-3.1-pro-preview" }, { label: "Gemini 3 Flash", value: "gemini-3-flash-preview" }, { label: "Gemini 3.1 Flash Lite", value: "gemini-3.1-flash-lite" }, { label: "Cancel", value: "Cancel" }],
2428
+ items: [{ label: "Gemma 4 31B (Default)", value: "gemma-4-31b-it" }, { label: "Gemini 3.1 Pro (Req. paid API Key)", value: "gemini-3.1-pro-preview" }, { label: "Gemini 3 Flash", value: "gemini-3-flash-preview" }, { label: "Gemini 3.1 Flash Lite", value: "gemini-3.1-flash-lite-preview" }, { label: "Cancel", value: "Cancel" }],
2375
2429
  onSelect: (item) => {
2376
2430
  if (item.value !== "Cancel") setActiveModel(item.value);
2377
2431
  setActiveView("chat");
@@ -2737,7 +2791,7 @@ OUTPUT: ${execOutputRef.current}`;
2737
2791
  return acc + Math.max(1, Math.ceil(line.length / wrapWidth));
2738
2792
  }, 0);
2739
2793
  const maxLines = Math.max(1, wrappedLines);
2740
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, statusText && /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "magenta", italic: true }, "\u23F3 ", statusText)), suggestions.length > 0 && /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "\u{1F4A1} Suggestions: "), suggestions.map((s, i) => /* @__PURE__ */ React8.createElement(Text8, { key: s, color: "yellow", bold: i === 0 }, " ", s, " "))), /* @__PURE__ */ React8.createElement(Box8, { backgroundColor: "#333333", paddingX: 1, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, maxLines > 3 ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "[\u{1F4E6} ", maxLines, " lines of text in buffer - Full content will be sent]"), /* @__PURE__ */ React8.createElement(
2794
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, null, statusText && /* @__PURE__ */ React8.createElement(Text8, { color: "magenta", italic: true }, "\u23F3 ", statusText)), /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "(", tempModelOverride || activeModel, ")")), suggestions.length > 0 && /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "\u{1F4A1} Suggestions: "), suggestions.map((s, i) => /* @__PURE__ */ React8.createElement(Text8, { key: s, color: "yellow", bold: i === 0 }, " ", s, " "))), /* @__PURE__ */ React8.createElement(Box8, { backgroundColor: "#333333", paddingX: 1, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, maxLines > 3 ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "[\u{1F4E6} ", maxLines, " lines of text in buffer - Full content will be sent]"), /* @__PURE__ */ React8.createElement(
2741
2795
  Box8,
2742
2796
  {
2743
2797
  flexDirection: "row",
@@ -2747,7 +2801,7 @@ OUTPUT: ${execOutputRef.current}`;
2747
2801
  alignItems: "flex-end"
2748
2802
  },
2749
2803
  /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")),
2750
- /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(
2804
+ /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1, position: "relative" }, input.split("\n").pop() === "" && !isProcessing && /* @__PURE__ */ React8.createElement(Box8, { position: "absolute", paddingLeft: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "Type your message...")), /* @__PURE__ */ React8.createElement(
2751
2805
  MultilineInput,
2752
2806
  {
2753
2807
  value: input.split("\n").pop() || "",
@@ -2758,14 +2812,13 @@ OUTPUT: ${execOutputRef.current}`;
2758
2812
  setInput(lines.join("\n"));
2759
2813
  },
2760
2814
  onSubmit: handleSubmit,
2761
- placeholder: escPressed ? "Press ESC again to cancel the request." : isProcessing ? "Flux Flow is thinking..." : "Type your message or /command...",
2762
2815
  keyBindings: {
2763
2816
  submit: (key) => key.return && !key.shift && !key.ctrl,
2764
2817
  newline: (key) => key.return && key.shift || key.return && key.ctrl
2765
2818
  }
2766
2819
  }
2767
- ))
2768
- )) : /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")), /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(
2820
+ )))
2821
+ )) : /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")), /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1, position: "relative" }, input === "" && !isProcessing && /* @__PURE__ */ React8.createElement(Box8, { position: "absolute", paddingLeft: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, escPressed ? " Press ESC again to cancel the request." : " Type your message or /command...")), /* @__PURE__ */ React8.createElement(
2769
2822
  MultilineInput,
2770
2823
  {
2771
2824
  value: input,
@@ -2774,17 +2827,16 @@ OUTPUT: ${execOutputRef.current}`;
2774
2827
  setInput(cleanVal);
2775
2828
  },
2776
2829
  onSubmit: handleSubmit,
2777
- placeholder: escPressed ? "Press ESC again to cancel the request." : isProcessing ? "Flux Flow is thinking..." : "Type your message or /command...",
2778
2830
  maxRows: 3,
2779
2831
  keyBindings: {
2780
2832
  submit: (key) => key.return && !key.shift && !key.ctrl,
2781
2833
  newline: (key) => key.return && key.shift || key.return && key.ctrl
2782
2834
  }
2783
2835
  }
2784
- ))))));
2836
+ )))))));
2785
2837
  }
2786
2838
  };
2787
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, messages.slice(0, completedIndex).map((msg, idx) => /* @__PURE__ */ React8.createElement(ChatLayout, { key: msg.id || idx, messages: [msg], showFullThinking }))), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", padding: 1, width: "100%" }, activeView === "chat" && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(ChatLayout, { messages: messages.slice(completedIndex), showFullThinking }), activeCommand && /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(TerminalBox, { command: activeCommand, output: execOutput }))), isInitializing ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "bold", borderColor: "yellow", padding: 1, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", bold: true }, "\u{1F511} API KEY REQUIRED"), /* @__PURE__ */ React8.createElement(Text8, null, "Please enter your Gemini API Key to initialize the agent's brain."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "\u276F "), /* @__PURE__ */ React8.createElement(
2839
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, messages.slice(0, completedIndex).map((msg, idx) => /* @__PURE__ */ React8.createElement(ChatLayout_default, { key: msg.id || idx, messages: [msg], showFullThinking }))), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", padding: 1, width: "100%" }, activeView === "chat" && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(ChatLayout_default, { messages: messages.slice(completedIndex), showFullThinking }), activeCommand && /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(TerminalBox, { command: activeCommand, output: execOutput }))), isInitializing ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "bold", borderColor: "yellow", padding: 1, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", bold: true }, "\u{1F511} API KEY REQUIRED"), /* @__PURE__ */ React8.createElement(Text8, null, "Please enter your Gemini API Key to initialize the agent's brain."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "\u276F "), /* @__PURE__ */ React8.createElement(
2788
2840
  TextInput2,
2789
2841
  {
2790
2842
  value: tempKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",