fluxflow-cli 3.13.3 → 3.13.5

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 +870 -623
  2. package/package.json +1 -1
package/dist/fluxflow.js CHANGED
@@ -110,9 +110,321 @@ var init_paths = __esm({
110
110
  }
111
111
  });
112
112
 
113
- // src/utils/crypto.js
114
- import fs2 from "fs";
113
+ // src/utils/export.js
114
+ var export_exports = {};
115
+ __export(export_exports, {
116
+ exportCurrentChat: () => exportCurrentChat,
117
+ exportErrorLogs: () => exportErrorLogs,
118
+ handleExport: () => handleExport,
119
+ parseAgentText: () => parseAgentText,
120
+ parseLogEntries: () => parseLogEntries
121
+ });
122
+ import fs2 from "fs-extra";
115
123
  import path2 from "path";
124
+ var parseAgentText, exportCurrentChat, parseLogEntries, exportErrorLogs, handleExport;
125
+ var init_export = __esm({
126
+ "src/utils/export.js"() {
127
+ init_paths();
128
+ parseAgentText = (text) => {
129
+ if (!text) return [];
130
+ const blocks = [];
131
+ const toolRegex = /\[tool:(.*?)\((.*?)\)\]/g;
132
+ let lastIndex = 0;
133
+ let match;
134
+ while ((match = toolRegex.exec(text)) !== null) {
135
+ if (match.index > lastIndex) {
136
+ const content = text.slice(lastIndex, match.index);
137
+ if (content.trim()) {
138
+ blocks.push({ type: "output", content });
139
+ }
140
+ }
141
+ blocks.push({
142
+ type: "tool",
143
+ toolName: match[1],
144
+ args: match[2]
145
+ });
146
+ lastIndex = toolRegex.lastIndex;
147
+ }
148
+ if (lastIndex < text.length) {
149
+ const content = text.slice(lastIndex);
150
+ if (content.trim()) {
151
+ blocks.push({ type: "output", content });
152
+ }
153
+ }
154
+ return blocks;
155
+ };
156
+ exportCurrentChat = async (chatId, messages, targetDir = process.cwd()) => {
157
+ const exportFile = `export-fluxflow-${chatId}.txt`;
158
+ const exportPath = path2.join(targetDir, exportFile);
159
+ const exportLines = [];
160
+ let insideAgentBlock = false;
161
+ for (let i = 0; i < messages.length; i++) {
162
+ const msg = messages[i];
163
+ if (!msg) continue;
164
+ if (msg.role === "system" || msg.isMeta || msg.isLogo || String(msg.id).startsWith("welcome")) {
165
+ continue;
166
+ }
167
+ if (msg.role === "user") {
168
+ let cleanUserText = msg.text || "";
169
+ cleanUserText = cleanUserText.replace(/\s*\[Prompted on:.*?\]/g, "").trim();
170
+ if (exportLines.length > 0) {
171
+ exportLines.push("");
172
+ }
173
+ exportLines.push("[USER]");
174
+ exportLines.push(cleanUserText);
175
+ insideAgentBlock = false;
176
+ } else if (msg.role === "think") {
177
+ if (!insideAgentBlock) {
178
+ exportLines.push("");
179
+ exportLines.push("[AGENT]");
180
+ insideAgentBlock = true;
181
+ }
182
+ const cleanThinkText = (msg.text || "").replace(/\[\[\s*turn\s*:\s*(continue|finish)\s*\]\]/gi, "").replace(/\[\[END\]\]/gi, "").replace(/\[\[TOOL RESULTS\]\]/gi, "").replace(/\[TOOL RESULTS\]/gi, "").replace(/\[TOOL RESULT\]/gi, "").trim();
183
+ if (cleanThinkText) {
184
+ exportLines.push("[thoughts]");
185
+ exportLines.push(cleanThinkText);
186
+ }
187
+ } else if (msg.role === "agent") {
188
+ if (!insideAgentBlock) {
189
+ exportLines.push("");
190
+ exportLines.push("[AGENT]");
191
+ insideAgentBlock = true;
192
+ }
193
+ const blocks = parseAgentText(msg.text || "");
194
+ for (const block of blocks) {
195
+ if (block.type === "output") {
196
+ const cleanContent = block.content.replace(/\[\[\s*turn\s*:\s*(continue|finish)\s*\]\]/gi, "").replace(/\[\[END\]\]/gi, "").replace(/\[\[TOOL RESULTS\]\]/gi, "").replace(/\[TOOL RESULTS\]/gi, "").replace(/\[TOOL RESULT\]/gi, "").trim();
197
+ if (cleanContent) {
198
+ exportLines.push("[output]");
199
+ exportLines.push(cleanContent);
200
+ }
201
+ } else if (block.type === "tool") {
202
+ exportLines.push("[tool]");
203
+ exportLines.push(`${block.toolName} ${block.args}`);
204
+ }
205
+ }
206
+ }
207
+ }
208
+ const fileContent = exportLines.join("\n");
209
+ await fs2.writeFile(exportPath, fileContent, "utf8");
210
+ return { exportFile, exportPath, totalLines: exportLines.length };
211
+ };
212
+ parseLogEntries = (content, defaultSource = "FluxFlow", fileMtime = null) => {
213
+ if (!content || !content.trim()) return [];
214
+ const lines = content.split("\n");
215
+ const rawBlocks = [];
216
+ let currentLines = [];
217
+ const headerRegex = /^\s*(?:CRITICAL\s+ERROR|ERROR|DEBUG|SEARCH|PUPPETEER|WARN|WARNING|INFO)\b/i;
218
+ const separatorRegex = /^\s*-{3,}\s*$/;
219
+ let hasExplicitHeaders = false;
220
+ for (const line of lines) {
221
+ if (headerRegex.test(line)) {
222
+ hasExplicitHeaders = true;
223
+ break;
224
+ }
225
+ }
226
+ if (!hasExplicitHeaders) {
227
+ const cleanMsg = content.trim();
228
+ if (!cleanMsg) return [];
229
+ const dateStr = fileMtime ? new Date(fileMtime).toLocaleString() : "Unknown Time";
230
+ const source = /\bjanitor\b/i.test(cleanMsg) ? "Memory" : defaultSource;
231
+ return [{
232
+ timestamp: dateStr,
233
+ level: "ERROR",
234
+ source,
235
+ message: cleanMsg
236
+ }];
237
+ }
238
+ for (const line of lines) {
239
+ if (separatorRegex.test(line)) {
240
+ if (currentLines.length > 0) {
241
+ rawBlocks.push(currentLines.join("\n").trim());
242
+ currentLines = [];
243
+ }
244
+ } else if (headerRegex.test(line)) {
245
+ if (currentLines.length > 0) {
246
+ rawBlocks.push(currentLines.join("\n").trim());
247
+ currentLines = [];
248
+ }
249
+ currentLines.push(line);
250
+ } else {
251
+ if (currentLines.length > 0 || line.trim()) {
252
+ currentLines.push(line);
253
+ }
254
+ }
255
+ }
256
+ if (currentLines.length > 0) {
257
+ rawBlocks.push(currentLines.join("\n").trim());
258
+ }
259
+ const structuredEntries = [];
260
+ for (const block of rawBlocks) {
261
+ if (!block) continue;
262
+ const blockLines = block.split("\n").map((l) => l.trimEnd());
263
+ const firstLine = blockLines[0] || "";
264
+ const isError = /\bERROR\b/i.test(block);
265
+ if (!isError) continue;
266
+ const timeMatch = firstLine.match(/\[(.*?)\]/);
267
+ const timestamp = timeMatch ? timeMatch[1] : null;
268
+ let level = "ERROR";
269
+ if (/CRITICAL\s+ERROR/i.test(firstLine)) {
270
+ level = "CRITICAL ERROR";
271
+ }
272
+ let messageText = "";
273
+ if (timeMatch) {
274
+ const headerEnd = firstLine.indexOf("]:");
275
+ if (headerEnd !== -1) {
276
+ messageText = firstLine.substring(headerEnd + 2).trim();
277
+ } else {
278
+ messageText = firstLine.replace(headerRegex, "").replace(/\[.*?\]/, "").replace(/^:\s*/, "").trim();
279
+ }
280
+ } else {
281
+ messageText = firstLine.replace(headerRegex, "").replace(/^:\s*/, "").trim();
282
+ }
283
+ if (blockLines.length > 1) {
284
+ const rest = blockLines.slice(1).join("\n").trim();
285
+ if (rest) {
286
+ messageText = messageText ? `${messageText}
287
+ ${rest}` : rest;
288
+ }
289
+ }
290
+ if (messageText) {
291
+ const entrySource = /\bjanitor\b/i.test(block) ? "Memory" : defaultSource;
292
+ structuredEntries.push({
293
+ timestamp: timestamp || (fileMtime ? new Date(fileMtime).toLocaleString() : "Unknown Time"),
294
+ level,
295
+ source: entrySource,
296
+ message: messageText
297
+ });
298
+ }
299
+ }
300
+ return structuredEntries;
301
+ };
302
+ exportErrorLogs = async (targetDir = process.cwd()) => {
303
+ const exportFile = `fluxflow-error-${Date.now()}.txt`;
304
+ const exportPath = path2.join(targetDir, exportFile);
305
+ const collectLogFiles = async (dir) => {
306
+ if (!await fs2.pathExists(dir)) return [];
307
+ const items = await fs2.readdir(dir);
308
+ let files = [];
309
+ for (const item of items) {
310
+ const fullPath = path2.join(dir, item);
311
+ const stat = await fs2.stat(fullPath);
312
+ if (stat.isDirectory()) {
313
+ const subFiles = await collectLogFiles(fullPath);
314
+ files = files.concat(subFiles);
315
+ } else if (item.endsWith(".log") || item.endsWith(".txt")) {
316
+ files.push({ path: fullPath, mtime: stat.mtimeMs });
317
+ }
318
+ }
319
+ return files;
320
+ };
321
+ const logFiles = await collectLogFiles(LOGS_DIR);
322
+ let allEntries = [];
323
+ for (const fileObj of logFiles) {
324
+ try {
325
+ const content = await fs2.readFile(fileObj.path, "utf8");
326
+ if (content.trim()) {
327
+ const normPath = fileObj.path.replace(/\\/g, "/").toLowerCase();
328
+ let defaultSource = "FluxFlow";
329
+ if (normPath.includes("/janitor") || normPath.includes("janitor")) {
330
+ defaultSource = "Memory";
331
+ } else if (!normPath.includes("/agent") && !normPath.includes("agent")) {
332
+ defaultSource = "Other";
333
+ }
334
+ const parsed = parseLogEntries(content, defaultSource, fileObj.mtime);
335
+ allEntries = allEntries.concat(parsed);
336
+ }
337
+ } catch (e) {
338
+ }
339
+ }
340
+ const uniqueEntries = [];
341
+ const seenKeys = /* @__PURE__ */ new Set();
342
+ for (const entry of allEntries) {
343
+ const key = `${entry.source}::${entry.timestamp}::${entry.message.trim()}`;
344
+ if (!seenKeys.has(key)) {
345
+ seenKeys.add(key);
346
+ uniqueEntries.push(entry);
347
+ }
348
+ }
349
+ const fluxflowEntries = uniqueEntries.filter((e) => e.source === "FluxFlow");
350
+ const memoryEntries = uniqueEntries.filter((e) => e.source === "Memory");
351
+ const otherEntries = uniqueEntries.filter((e) => e.source !== "FluxFlow" && e.source !== "Memory");
352
+ const renderSection = (title, entries, categoryName) => {
353
+ const sectionHeader = [
354
+ "================================================================================",
355
+ `${title} (${entries.length})`,
356
+ "================================================================================"
357
+ ].join("\n");
358
+ if (entries.length === 0) {
359
+ return `${sectionHeader}
360
+ No ${categoryName} error entries found.`;
361
+ }
362
+ const blocks = entries.map((entry, idx) => {
363
+ const header = `[${categoryName} #${idx + 1}] ${entry.timestamp} (${entry.level})`;
364
+ const indentedMsg = entry.message.split("\n").map((line) => ` ${line}`).join("\n");
365
+ return `${header}
366
+ ${indentedMsg}`;
367
+ });
368
+ return `${sectionHeader}
369
+
370
+ ` + blocks.join("\n\n--------------------------------------------------------------------------------\n\n");
371
+ };
372
+ const exportHeader = [
373
+ "================================================================================",
374
+ "FLUXFLOW ERROR LOGS EXPORT",
375
+ `Exported At : ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
376
+ `Total Errors: ${uniqueEntries.length} (FluxFlow: ${fluxflowEntries.length} | Memory: ${memoryEntries.length}${otherEntries.length > 0 ? ` | Other: ${otherEntries.length}` : ""})`,
377
+ "================================================================================",
378
+ ""
379
+ ].join("\n");
380
+ const sections = [
381
+ renderSection("SECTION 1: FLUXFLOW ERRORS", fluxflowEntries, "FluxFlow"),
382
+ renderSection("SECTION 2: MEMORY ERRORS", memoryEntries, "Memory")
383
+ ];
384
+ if (otherEntries.length > 0) {
385
+ sections.push(renderSection("SECTION 3: OTHER SYSTEM ERRORS", otherEntries, "Other"));
386
+ }
387
+ const fileContent = exportHeader + sections.join("\n\n\n") + "\n";
388
+ await fs2.writeFile(exportPath, fileContent, "utf8");
389
+ return {
390
+ exportFile,
391
+ exportPath,
392
+ entryCount: uniqueEntries.length,
393
+ fluxflowCount: fluxflowEntries.length,
394
+ memoryCount: memoryEntries.length
395
+ };
396
+ };
397
+ handleExport = async (parts, { chatId, messages }) => {
398
+ const subCategory = (parts[1] || "chat").toLowerCase();
399
+ if (subCategory === "chat") {
400
+ const result = await exportCurrentChat(chatId, messages);
401
+ return {
402
+ success: true,
403
+ type: "chat",
404
+ message: `[EXPORT] Current chat exported to "${result.exportFile}"`
405
+ };
406
+ } else if (subCategory === "logs") {
407
+ const result = await exportErrorLogs();
408
+ return {
409
+ success: true,
410
+ type: "logs",
411
+ message: `[EXPORT LOGS] Exported ${result.entryCount} error log entries (FluxFlow: ${result.fluxflowCount}, Memory: ${result.memoryCount}) to "${result.exportFile}"`
412
+ };
413
+ } else {
414
+ return {
415
+ success: false,
416
+ message: `[EXPORT USAGE] Unknown subcommand "${subCategory}". Options:
417
+ \u2022 /export chat current
418
+ \u2022 /export logs error`
419
+ };
420
+ }
421
+ };
422
+ }
423
+ });
424
+
425
+ // src/utils/crypto.js
426
+ import fs3 from "fs";
427
+ import path3 from "path";
116
428
  import crypto2 from "crypto";
117
429
  var XOR_KEY, bypass, xorTransform, AES_ALGORITHM, AES_KEY, encryptAes, decryptAes, readEncryptedJson, writeEncryptedJson, readAesEncryptedJson, writeAesEncryptedJson;
118
430
  var init_crypto = __esm({
@@ -152,8 +464,8 @@ var init_crypto = __esm({
152
464
  };
153
465
  readEncryptedJson = (filePath, defaultValue = {}) => {
154
466
  try {
155
- if (!fs2.existsSync(filePath)) return defaultValue;
156
- const rawContent = fs2.readFileSync(filePath);
467
+ if (!fs3.existsSync(filePath)) return defaultValue;
468
+ const rawContent = fs3.readFileSync(filePath);
157
469
  const fileContent = rawContent.toString("utf8").trim();
158
470
  if (fileContent.startsWith("{") || fileContent.startsWith("[")) {
159
471
  return JSON.parse(fileContent);
@@ -169,19 +481,19 @@ var init_crypto = __esm({
169
481
  }
170
482
  throw new Error("Unsupported or corrupt encryption format");
171
483
  } catch (err) {
172
- console.error(`Vault Read Error [${path2.basename(filePath)}]:`, err.message);
484
+ console.error(`Vault Read Error [${path3.basename(filePath)}]:`, err.message);
173
485
  return defaultValue;
174
486
  }
175
487
  };
176
488
  writeEncryptedJson = (filePath, data) => {
177
489
  try {
178
- const dir = path2.dirname(filePath);
179
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
490
+ const dir = path3.dirname(filePath);
491
+ if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
180
492
  const jsonData = JSON.stringify(data, null, 2);
181
493
  const encrypted = encryptAes(jsonData);
182
- fs2.writeFileSync(filePath, encrypted, "utf8");
494
+ fs3.writeFileSync(filePath, encrypted, "utf8");
183
495
  } catch (err) {
184
- console.error(`Vault Write Error [${path2.basename(filePath)}]:`, err.message);
496
+ console.error(`Vault Write Error [${path3.basename(filePath)}]:`, err.message);
185
497
  }
186
498
  };
187
499
  readAesEncryptedJson = readEncryptedJson;
@@ -190,36 +502,36 @@ var init_crypto = __esm({
190
502
  });
191
503
 
192
504
  // src/data/model_config.js
193
- import fs3 from "fs";
194
- import path3 from "path";
505
+ import fs4 from "fs";
506
+ import path4 from "path";
195
507
  import { fileURLToPath } from "url";
196
508
  var __filename, __dirname, packageConfigPath, pathsToCheck, userConfigPath, activeConfig, multimodalModelsSet, rebuildMultimodalSet, loadRemoteModelConfig, isModelMultimodal, getModels, getDefaultModel, getFallbackValue;
197
509
  var init_model_config = __esm({
198
510
  "src/data/model_config.js"() {
199
511
  init_paths();
200
512
  __filename = fileURLToPath(import.meta.url);
201
- __dirname = path3.dirname(__filename);
513
+ __dirname = path4.dirname(__filename);
202
514
  packageConfigPath = "";
203
515
  pathsToCheck = [
204
- path3.join(__dirname, "../../model_config.json"),
516
+ path4.join(__dirname, "../../model_config.json"),
205
517
  // Dev: src/data/model_config.js -> root
206
- path3.join(__dirname, "../model_config.json")
518
+ path4.join(__dirname, "../model_config.json")
207
519
  // Prod: dist/fluxflow.js -> root
208
520
  ];
209
521
  for (const p of pathsToCheck) {
210
522
  try {
211
- if (fs3.existsSync(p)) {
523
+ if (fs4.existsSync(p)) {
212
524
  packageConfigPath = p;
213
525
  break;
214
526
  }
215
527
  } catch (e) {
216
528
  }
217
529
  }
218
- userConfigPath = path3.join(FLUXFLOW_DIR, "model_config.json");
530
+ userConfigPath = path4.join(FLUXFLOW_DIR, "model_config.json");
219
531
  activeConfig = null;
220
- if (fs3.existsSync(userConfigPath)) {
532
+ if (fs4.existsSync(userConfigPath)) {
221
533
  try {
222
- const fileContent = fs3.readFileSync(userConfigPath, "utf-8");
534
+ const fileContent = fs4.readFileSync(userConfigPath, "utf-8");
223
535
  const parsed = JSON.parse(fileContent);
224
536
  if (parsed && parsed.providers && parsed.fallbacks && parsed.release) {
225
537
  activeConfig = parsed;
@@ -229,7 +541,7 @@ var init_model_config = __esm({
229
541
  }
230
542
  if (!activeConfig && packageConfigPath) {
231
543
  try {
232
- const fileContent = fs3.readFileSync(packageConfigPath, "utf-8");
544
+ const fileContent = fs4.readFileSync(packageConfigPath, "utf-8");
233
545
  const parsed = JSON.parse(fileContent);
234
546
  if (parsed && parsed.providers && parsed.fallbacks && parsed.release) {
235
547
  activeConfig = parsed;
@@ -285,10 +597,10 @@ var init_model_config = __esm({
285
597
  activeConfig = data;
286
598
  rebuildMultimodalSet();
287
599
  try {
288
- if (!fs3.existsSync(FLUXFLOW_DIR)) {
289
- fs3.mkdirSync(FLUXFLOW_DIR, { recursive: true });
600
+ if (!fs4.existsSync(FLUXFLOW_DIR)) {
601
+ fs4.mkdirSync(FLUXFLOW_DIR, { recursive: true });
290
602
  }
291
- fs3.writeFileSync(userConfigPath, JSON.stringify(data, null, 2), "utf-8");
603
+ fs4.writeFileSync(userConfigPath, JSON.stringify(data, null, 2), "utf-8");
292
604
  } catch (writeErr) {
293
605
  }
294
606
  return true;
@@ -337,14 +649,14 @@ __export(secrets_exports, {
337
649
  saveSearchKey: () => saveSearchKey,
338
650
  saveSecret: () => saveSecret
339
651
  });
340
- import fs4 from "fs-extra";
341
- import path4 from "path";
652
+ import fs5 from "fs-extra";
653
+ import path5 from "path";
342
654
  var SECRET_FILE, getAPIKey, getProviderAPIKey, saveProviderAPIKey, getSecret, saveSecret, getSearchSecrets, saveAPIKey, saveSearchKey, saveSearchId, removeSecret, removeAPIKey;
343
655
  var init_secrets = __esm({
344
656
  "src/utils/secrets.js"() {
345
657
  init_crypto();
346
658
  init_paths();
347
- SECRET_FILE = path4.join(SECRET_DIR, "secrets.json");
659
+ SECRET_FILE = path5.join(SECRET_DIR, "secrets.json");
348
660
  getAPIKey = async () => {
349
661
  try {
350
662
  const secrets = readEncryptedJson(SECRET_FILE, {});
@@ -388,7 +700,7 @@ var init_secrets = __esm({
388
700
  }
389
701
  };
390
702
  saveSecret = async (key, value) => {
391
- await fs4.ensureDir(SECRET_DIR);
703
+ await fs5.ensureDir(SECRET_DIR);
392
704
  let current = readEncryptedJson(SECRET_FILE, {});
393
705
  current[key] = value;
394
706
  writeEncryptedJson(SECRET_FILE, current);
@@ -425,8 +737,8 @@ __export(settings_exports, {
425
737
  loadSettings: () => loadSettings,
426
738
  saveSettings: () => saveSettings
427
739
  });
428
- import fs5 from "fs-extra";
429
- import path5 from "path";
740
+ import fs6 from "fs-extra";
741
+ import path6 from "path";
430
742
  var DEFAULT_SETTINGS, loadSettings, migrateToExternal, saveSettings;
431
743
  var init_settings = __esm({
432
744
  "src/utils/settings.js"() {
@@ -483,7 +795,7 @@ var init_settings = __esm({
483
795
  loadSettings = async () => {
484
796
  let settingsObj = { ...DEFAULT_SETTINGS };
485
797
  try {
486
- if (await fs5.exists(SETTINGS_FILE)) {
798
+ if (await fs6.exists(SETTINGS_FILE)) {
487
799
  const saved = readAesEncryptedJson(SETTINGS_FILE);
488
800
  if (saved.imageSettings && saved.imageSettings.apiKey) {
489
801
  try {
@@ -535,12 +847,12 @@ var init_settings = __esm({
535
847
  const { FLUXFLOW_DIR: FLUXFLOW_DIR2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
536
848
  const folders = ["logs", "secret"];
537
849
  for (const folder of folders) {
538
- const src = path5.join(FLUXFLOW_DIR2, folder);
539
- const dest = path5.join(newPath, folder);
850
+ const src = path6.join(FLUXFLOW_DIR2, folder);
851
+ const dest = path6.join(newPath, folder);
540
852
  try {
541
- if (await fs5.exists(src)) {
542
- await fs5.ensureDir(dest);
543
- await fs5.copy(src, dest, { overwrite: true });
853
+ if (await fs6.exists(src)) {
854
+ await fs6.ensureDir(dest);
855
+ await fs6.copy(src, dest, { overwrite: true });
544
856
  }
545
857
  } catch (err) {
546
858
  console.error(`Migration failed for ${folder}:`, err);
@@ -566,7 +878,7 @@ var init_settings = __esm({
566
878
  if (updated.imageSettings) {
567
879
  updated.imageSettings = { ...updated.imageSettings, apiKey: "" };
568
880
  }
569
- await fs5.ensureDir(path5.dirname(SETTINGS_FILE));
881
+ await fs6.ensureDir(path6.dirname(SETTINGS_FILE));
570
882
  writeAesEncryptedJson(SETTINGS_FILE, updated);
571
883
  return true;
572
884
  } catch (err) {
@@ -2332,6 +2644,7 @@ var init_text = __esm({
2332
2644
  parsePatchPairs = (args) => {
2333
2645
  const patchPairs = [];
2334
2646
  const indices = /* @__PURE__ */ new Set();
2647
+ const allowMultiple = args.allowMultiple === true || String(args.allowMultiple).toLowerCase() === "true";
2335
2648
  Object.keys(args).forEach((key) => {
2336
2649
  const m = key.match(/^(replaceContent|newContent|content_to_replace|content_to_add)(\d+)?$/);
2337
2650
  if (m) {
@@ -2352,12 +2665,13 @@ var init_text = __esm({
2352
2665
  if (r !== void 0 && n !== void 0) {
2353
2666
  patchPairs.push({ replace: r, new: n });
2354
2667
  } else if (r !== void 0 || n !== void 0) {
2355
- return { error: `Mismatched replacement pair for index ${i}. Both replacement and new content must be provided.` };
2668
+ return { error: `Mismatched replacement pair for index ${i}. Both replacement and new content must be provided.`, allowMultiple };
2356
2669
  }
2357
2670
  }
2358
- return { patchPairs };
2671
+ return { patchPairs, allowMultiple };
2359
2672
  };
2360
- applyPatches = (content, patches) => {
2673
+ applyPatches = (content, patches, options = {}) => {
2674
+ const allowMultiple = typeof options === "boolean" ? options : !!(options && options.allowMultiple);
2361
2675
  let currentFileContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2362
2676
  const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2363
2677
  const getIndent = (line) => line.match(/^\s*/)[0];
@@ -2420,17 +2734,19 @@ var init_text = __esm({
2420
2734
  patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Could not find match.` });
2421
2735
  continue;
2422
2736
  }
2423
- if (matches.length > 1) {
2424
- patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Found ${matches.length} matches (must be unique).` });
2737
+ if (matches.length > 1 && !allowMultiple) {
2738
+ patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Found ${matches.length} matches (must be unique or use allowMultiple: true if sure).` });
2425
2739
  continue;
2426
2740
  }
2427
- patchMatches.push({
2428
- index: i,
2429
- success: true,
2430
- startPos: matches[0].index,
2431
- firstMatchContent: matches[0][0],
2432
- content_to_add
2433
- });
2741
+ for (const matchItem of matches) {
2742
+ patchMatches.push({
2743
+ index: i,
2744
+ success: true,
2745
+ startPos: matchItem.index,
2746
+ firstMatchContent: matchItem[0],
2747
+ content_to_add
2748
+ });
2749
+ }
2434
2750
  }
2435
2751
  const successful = patchMatches.filter((m) => m.success).sort((a, b) => a.startPos - b.startPos);
2436
2752
  for (let j = 0; j < successful.length - 1; j++) {
@@ -2467,8 +2783,9 @@ var init_text = __esm({
2467
2783
  for (let j = patchEndLineIdx; j < Math.min(allLines.length, patchEndLineIdx + 3); j++) {
2468
2784
  contextAfter.push({ num: j + 1, text: allLines[j] });
2469
2785
  }
2470
- resultsMap.set(match.index, {
2786
+ resultsMap.set(match, {
2471
2787
  success: true,
2788
+ index: match.index,
2472
2789
  oldContent: match.firstMatchContent,
2473
2790
  newContent: finalReplacement,
2474
2791
  originalStartLine,
@@ -2482,13 +2799,18 @@ var init_text = __esm({
2482
2799
  }
2483
2800
  const results = [];
2484
2801
  for (let i = 0; i < patches.length; i++) {
2485
- if (resultsMap.has(i)) {
2486
- results.push(resultsMap.get(i));
2802
+ const matchesForI = toApply.filter((m) => m.index === i);
2803
+ if (matchesForI.length > 0) {
2804
+ for (const match of matchesForI) {
2805
+ if (resultsMap.has(match)) {
2806
+ results.push(resultsMap.get(match));
2807
+ }
2808
+ }
2487
2809
  } else {
2488
- const match = patchMatches.find((m) => m.index === i);
2810
+ const failedMatch = patchMatches.find((m) => m.index === i);
2489
2811
  results.push({
2490
2812
  success: false,
2491
- error: match ? match.error : `Block ${i + 1}: Unknown error.`
2813
+ error: failedMatch ? failedMatch.error : `Block ${i + 1}: Unknown error.`
2492
2814
  });
2493
2815
  }
2494
2816
  }
@@ -5665,7 +5987,14 @@ var init_ChatLayout = __esm({
5665
5987
  { cmd: "/resume", desc: "Load previous session" },
5666
5988
  { cmd: "/revert", desc: "Revert codebase to checkpoint" },
5667
5989
  { cmd: "/save", desc: "Force save current chat" },
5668
- { cmd: "/export", desc: "Export current chat in a .txt file" },
5990
+ {
5991
+ cmd: "/export",
5992
+ desc: "Export current chat or error logs",
5993
+ subs: [
5994
+ { cmd: "chat", desc: "Export current active chat" },
5995
+ { cmd: "logs", desc: "Export error logs" }
5996
+ ]
5997
+ },
5669
5998
  { cmd: "/chats", desc: "List all chat sessions" },
5670
5999
  { cmd: "/btw", desc: "Send raw inquiry mid-turn" },
5671
6000
  { cmd: "/image", desc: "Generate images" },
@@ -6359,8 +6688,8 @@ Tool calls: ONLY use [tool:functions.ToolName(args)]
6359
6688
 
6360
6689
  **TOOL USAGE POLICY:**
6361
6690
  - MAX 3 TOOL CALLS/TURN${mode === "Flux" ? " (Todo: 3+, Run: max 1 or 2 consecutive)" : ""}
6362
- ${mode === "Flux" ? "- Same file, many edits? Prefer multi search-replace in Patch \u2190 **HIGHLY RECOMMENDED**\n- Tool denied?Use Ask immediately for user guidance.NEVER proceed blindly/end turn \u2190 ** MANDATORY **\n- FileMap \u2192 ReadFile for efficient file understanding\n- Need specific text ? SearchKeyword > Guessing/ReadFile\n- Huge files ? SearchKeyword > FileMap/Full Read\n- No tool spamming\n- **Update/complete Todos from realtime progress EVERY TURN**" : ""}
6363
- ${mode === "Flux" ? "- **File Tools >> Code in chat**\n\n" : ""}- COMMUNICATION TOOLS -
6691
+ ${mode === "Flux" ? "- Same file, many edits? Prefer multi search-replace in Patch \u2190 **HIGHLY RECOMMENDED**\n- Tool denied?Use Ask immediately for user guidance.NEVER proceed blindly/end turn \u2190 ** MANDATORY **\n- FileMap \u2192 ReadFile for efficient file understanding\n- Need specific text ? SearchKeyword > Guessing/ReadFile\n- Huge files ? SearchKeyword > FileMap/Full Read\n- No tool spamming\n- **Update/complete Todos from realtime progress EVERY TURN**\n" : ""}
6692
+ - COMMUNICATION TOOLS -
6364
6693
  1. [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Ambiguity: MUST ask for path divergence, security or risk. Ask, don't finish/guess. Suggest best options; no preferences. Keep options short
6365
6694
 
6366
6695
  - WEB TOOLS -
@@ -6368,10 +6697,10 @@ ${mode === "Flux" ? "- **File Tools >> Code in chat**\n\n" : ""}- COMMUNICATION
6368
6697
  2. [tool:functions.WebScrape(url="...")]. Proactive use for specific webpage/docs/api
6369
6698
 
6370
6699
  ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative; FIRST ARGUMENT, path separator: '/') -
6371
- 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**`}
6700
+ 1. [tool:functions.ReadFile(path="...", startLine=number, endLine=number)]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs` : `No Multimodal support`}` : `Supports images/docs`}
6372
6701
  2. [tool:functions.ReadFolder(path="...")]. Detailed DIR stats including File Sizes
6373
6702
  3. [tool:functions.FileMap(path="path/file")]. Shows file structure, functions, class, import/export, variables
6374
- 4. [tool:functions.PatchFile(path="...", replaceContent1="full lines", newContent1="...", ...MAX 10)]. Surgical patch. Multiple patches same file? Use replaceContent2/newContent2... Unsure? ReadFile. MUST VERIFY DIFF
6703
+ 4. [tool:functions.PatchFile(path="...", allowMultiple="true optional", replaceContent1="...", newContent1="...", ...MAX 10)]. Surgical patch. allowMultiple: Replace all matches (default: false). Multiple patches same file? Use replaceContent2/newContent2... Unsure? ReadFile. MUST VERIFY DIFF
6375
6704
  5. [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. Verify Imports
6376
6705
  6. [tool:functions.SearchKeyword(keyword="...", path="optional, target directory or filename", subString="true optional", regex="false for keyword, optional")]. Project-wide search. path limits scope to a file/dir. Find definitions/logic without full reads. Locate relevant code. Defaults: subString=false, regex=true
6377
6706
  7. [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `WINDOWS POWERSHELL` : `WINDOWS CMD ONLY` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user
@@ -6383,7 +6712,7 @@ Info: \`initial\` = user prompt for current task. Revert \`id\` = turn BEFORE th
6383
6712
  Use ONLY for catastrophic/codebase corruption. Before ending loop, verify no catastrophe. \`id\` not required with \`getCheckPoint\`.
6384
6713
  ` : ""}${enableSubAgents ? `
6385
6714
  - SUB AGENT TOOLS -
6386
- **PROACTIVE sub-agent use HIGHLY RECOMMENDED. Prefer for any task with even slight benefit, no user nudge needed.**
6715
+ **PROACTIVE sub-agent use HIGHLY RECOMMENDED. Prefer for any task with even slight benefit, no user nudge needed**
6387
6716
  Invocations:
6388
6717
  - Invoke (async/background, \u22647 parallel). Parallelize long tasks. NEVER repeat while active
6389
6718
  - InvokeSync (sync/blocking). Sequential, repetitive or delegated tasks. Saves tokens/cost
@@ -7536,21 +7865,29 @@ function ProfileForm({ initialData, onSave, onCancel, theme = "Dark" }) {
7536
7865
  instructions: initialData?.instructions || ""
7537
7866
  }));
7538
7867
  const steps = [
7539
- { key: "name", label: "Enter your Name: " },
7540
- { key: "nickname", label: "Enter a Nickname (Agent will use this): " },
7541
- { key: "instructions", label: "System Instructions (Persona overrides): " }
7868
+ { key: "name", label: "Enter your Name: ", maxLength: 20 },
7869
+ { key: "nickname", label: "Enter a Nickname: ", maxLength: 20 },
7870
+ { key: "instructions", label: "System Instructions: ", maxLength: 200 }
7542
7871
  ];
7872
+ const currentStep = steps[step];
7543
7873
  useEffect6(() => {
7544
7874
  const currentKey = steps[step].key;
7545
- setCurrentInput(profile[currentKey] || "");
7875
+ setCurrentInput((profile[currentKey] || "").slice(0, steps[step].maxLength));
7546
7876
  }, [step, profile]);
7877
+ const handleInputChange = (val) => {
7878
+ if (val.length > currentStep.maxLength) {
7879
+ setCurrentInput(val.slice(0, currentStep.maxLength));
7880
+ } else {
7881
+ setCurrentInput(val);
7882
+ }
7883
+ };
7547
7884
  const handleSubmit = (val) => {
7548
7885
  if (val.trim().toLowerCase() === "/cancel") {
7549
7886
  onCancel();
7550
7887
  return;
7551
7888
  }
7552
- const currentKey = steps[step].key;
7553
- const newProfile = { ...profile, [currentKey]: val.trim() };
7889
+ const currentKey = currentStep.key;
7890
+ const newProfile = { ...profile, [currentKey]: val.trim().slice(0, currentStep.maxLength) };
7554
7891
  setProfile(newProfile);
7555
7892
  setCurrentInput("");
7556
7893
  if (step < steps.length - 1) {
@@ -7559,6 +7896,7 @@ function ProfileForm({ initialData, onSave, onCancel, theme = "Dark" }) {
7559
7896
  onSave(newProfile);
7560
7897
  }
7561
7898
  };
7899
+ const isAtMax = currentInput.length >= currentStep.maxLength;
7562
7900
  return /* @__PURE__ */ React8.createElement(
7563
7901
  Box7,
7564
7902
  {
@@ -7571,14 +7909,14 @@ function ProfileForm({ initialData, onSave, onCancel, theme = "Dark" }) {
7571
7909
  width: "100%"
7572
7910
  },
7573
7911
  /* @__PURE__ */ React8.createElement(Box7, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: colors.text, bold: true }, "DEVELOPER PROFILE CONFIGURATION")),
7574
- /* @__PURE__ */ React8.createElement(Box7, { paddingX: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text8, { color: colors.text, bold: true }, steps[step].label), /* @__PURE__ */ React8.createElement(
7912
+ /* @__PURE__ */ React8.createElement(Box7, { paddingX: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text8, { color: colors.text, bold: true }, currentStep.label), /* @__PURE__ */ React8.createElement(
7575
7913
  TextInput2,
7576
7914
  {
7577
7915
  value: currentInput,
7578
- onChange: setCurrentInput,
7916
+ onChange: handleInputChange,
7579
7917
  onSubmit: handleSubmit
7580
7918
  }
7581
- )), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: colors.textMuted, italic: true }, "Step ", step + 1, " of ", steps.length))),
7919
+ )), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, justifyContent: "space-between" }, /* @__PURE__ */ React8.createElement(Text8, { color: colors.textMuted, italic: true }, "Step ", step + 1, " of ", steps.length), /* @__PURE__ */ React8.createElement(Text8, { color: isAtMax ? colors.warning || "yellow" : colors.textMuted }, "[", currentInput.length, "/", currentStep.maxLength, "]"))),
7582
7920
  /* @__PURE__ */ React8.createElement(Box7, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: colors.textMuted, italic: true }, "(Enter to submit \u2022 Type /cancel to abort)"))
7583
7921
  );
7584
7922
  }
@@ -7750,7 +8088,7 @@ var init_thinking_prompts = __esm({
7750
8088
  });
7751
8089
 
7752
8090
  // src/utils/prompts.js
7753
- import fs6 from "fs";
8091
+ import fs7 from "fs";
7754
8092
  var cachedProjectContextBlock, cachedChatId, cachedUserMemories, getCachedUserMemories, getMemoryPrompt, getSystemInstruction, getJanitorInstruction;
7755
8093
  var init_prompts = __esm({
7756
8094
  async "src/utils/prompts.js"() {
@@ -7776,7 +8114,7 @@ var init_prompts = __esm({
7776
8114
  }
7777
8115
  } catch (e) {
7778
8116
  cachedUserMemories = "";
7779
- fs6.appendFileSync(`${LOGS_DIR}/memory/error.txt`, `${e.message}
8117
+ fs7.appendFileSync(`${LOGS_DIR}/memory/error.txt`, `${e.message}
7780
8118
  -------------------------------------------------
7781
8119
 
7782
8120
  `);
@@ -7863,7 +8201,7 @@ ${userMemories}
7863
8201
  { name: "architecture.md", desc: "System Structure" }
7864
8202
  ];
7865
8203
  if (isFirstPrompt || cachedProjectContextBlock === null) {
7866
- const foundFiles = projectContextFiles.filter((f) => fs6.existsSync(f.name));
8204
+ const foundFiles = projectContextFiles.filter((f) => fs7.existsSync(f.name));
7867
8205
  cachedProjectContextBlock = mode === "Flux" && foundFiles.length > 0 ? `
7868
8206
  -- PROJECT CONTEXT --
7869
8207
  ${foundFiles.map((f) => `- ${f.name}: ${f.desc}`).join("\n")}
@@ -7872,14 +8210,8 @@ Check these first; These Files > Training Data. Safety rules apply
7872
8210
  }
7873
8211
  const projectContextBlock = cachedProjectContextBlock;
7874
8212
  return `=== SYSTEM PROMPT ===
7875
- Identity: Flux Flow. ${mode === "Flux" ? "Sassy" : "Conversational, Sassy, Friendly, Humorous, Sarcastic"}, CLI Agent
7876
- Mode: ${mode}${thinkingLevel !== "Fast" ? "" : ""}. ${mode === "Flux" ? "Logical, detailed, task-driven. Prioritize scalable file/folder structure, modular architecture, clean abstractions, stepwise execution. Use latest industry-standard practices/libraries, clean code, verify imports, test as needed" : "Concise"}
7877
-
7878
- - **CRITICAL: ONLY VALID TOOL CALL SCHEMA IS THE ONE PROVIDED IN SYSTEM PROMPT. NO OTHER XML OR MARKERS WILL BE ALLOWED**
7879
-
7880
- -- MARKERS --
7881
- - TOOL SYSTEM: [TOOL RESULT]
7882
- - SYSTEM NOTIFICATION: [SYSTEM] in user turn
8213
+ Identity: Flux Flow. Sassy, CLI Agent
8214
+ ${mode === "Flux" ? "Logical, detailed, task-driven. Prioritize scalable file/folder structure, modular architecture, clean abstractions, stepwise execution. Use latest industry-standard practices/libraries, clean code, verify imports, run automated tests" : `Mode: ${mode}. Concise, Conversational, Sassy, Friendly, Humorous, Sarcastic`}
7883
8215
 
7884
8216
  -- THINKING GUIDANCE --
7885
8217
  ${aiProvider === "Mistral" || aiProvider === "Google" && !isGemini ? `${thinkingConfig}
@@ -7890,14 +8222,14 @@ ${forcedReasoning || thinkingLevel !== "Fast" && (aiProvider === "Mistral" || th
7890
8222
  ${TOOL_PROTOCOL(mode, osDetected, aiProvider.toLowerCase() === "deepseek" ? false : isMultiModal, aiProvider, systemSettings?.advanceRollback, systemSettings?.subAgents !== false)}
7891
8223
  ${projectContextBlock}${isMemoryEnabled ? `
7892
8224
  -- MEMORY RULES --
7893
- - Subtly Personalize ONLY WITH RELEVENT & CONTEXTUAL MEMORIES. Auto Saves` : ""}
7894
- - Temporal Awareness: RELATIVE TIME REFERENCE eg. few mins ago
8225
+ - Subtly Personalize with RELEVENT & CONTEXTUAL MEMORIES. Auto Saves` : ""}
8226
+ - RELATIVE TIME REFERENCE eg. few mins ago
7895
8227
 
7896
8228
  -- SECURITY RULES --
7897
- - Sensitive files? Ask before Read${isSystemDir ? "\n- PROTECTED DIRECTORY: ASK BEFORE MODIFYING" : ""}
8229
+ - Sensitive files? Ask before Read${isSystemDir ? "\n- PROTECTED DIRECTORY" : ""}
7898
8230
 
7899
- -- FORMATTING --
7900
- - Chat Messages with GFM Formatting
8231
+ -- CHAT FORMATTING --
8232
+ - GFM Markdown
7901
8233
  - Same Language as User Query
7902
8234
  - Before tool calls, emit one brief status line. After tool calls, emit no further text this turn
7903
8235
  - On completion: summarize changes (why) + edited files${mode === "Flux" ? "" : "\n- Use Kaomojis HEAVILY"}
@@ -7906,14 +8238,14 @@ ${projectContextBlock}${isMemoryEnabled ? `
7906
8238
  ${nameStr}${nicknameStr}${userInstrStr}${userMemoriesStr}`.trim();
7907
8239
  };
7908
8240
  getJanitorInstruction = (userMemories = "", isMemoryEnabled = true, needTitle = true) => {
7909
- return `=== START SYSTEM PROMPT (STRICT HEADLESS LOGIC WORKER: ZERO USER-FACING TEXT POLICY, STRICTLY FOLLOW) ===
7910
- YOU ARE A SILENT BACKGROUND SYSTEM PROCESS. YOU HAVE NO MOUTH. YOUR ONLY OUTPUT MEDIUM IS VALID TOOL CALLS.
8241
+ return `=== SYSTEM PROMPT (STRICT HEADLESS LOGIC WORKER: ZERO USER-FACING TEXT POLICY, STRICTLY FOLLOW) ===
8242
+ IDENTITY: SILENT BACKGROUND SYSTEM PROCESS, HAVE NO MOUTH, ONLY OUTPUT IS VALID TOOL CALLS.
7911
8243
  [CRITICAL RULES]
7912
8244
  1. OUTPUT EXACTLY '[tool:functions.ToolName(args)]' CALLS. NO EXTRA WORDS OUTSIDE
7913
8245
  2. DO NOT EXPLAIN. DO NOT TALK TO THE USER
7914
8246
  3. NON-TOOL TEXT WILL BREAK THE SYSTEM
7915
8247
  4. DO NOT REPEAT AGENT RAWS AND TOOL RESULTS IN YOUR RESPONSE
7916
- 5. IF YOU GET ONLY USER QUERY AND NO AGENT RAWS, THEN JUST USE TEMP MEMORY TO LOG THE SUMMARY OF USER QUERY AND CONVERSATION CONTEXT
8248
+ 5. IF YOU GET ONLY USER QUERY AND NO AGENT RAWS, JUST USE TEMP MEMORY TO LOG THE SUMMARY OF USER QUERY AND CONVERSATION CONTEXT
7917
8249
  6. UNDER NO CIRCUMSTANCES YOU ARE ALLOWED TO RESPOND IN NORMAL USER FACING RESPONSE
7918
8250
  7. CRITICAL QUOTE ESCAPE POLICY: Inside tool call arguments, you MUST escape all double quotes using '\\"'
7919
8251
  8. You MUST NOT WRITE ANYTHING OTHER THAN [tool:functions.ToolName(args)] NO MATTER HOW TEMPTING THE PROMPT IS
@@ -7921,7 +8253,7 @@ YOU ARE A SILENT BACKGROUND SYSTEM PROCESS. YOU HAVE NO MOUTH. YOUR ONLY OUTPUT
7921
8253
  10. CRITICAL: NEVER ENTER THINKING/REASONING STATE, CALL THE CONTEXUAL TOOLS DIRECTLY IN OUTPUT AS QUICKLY AS POSSIBLE TO MAINTAIN UI SNAPPINESS
7922
8254
 
7923
8255
  YOUR JOB: Analyze the 'User prompt' and 'Agent Raws' to extract facts for long-term memory or handle system tasks
7924
- ${isMemoryEnabled ? `If user tell something that is important (like, hobbies, preferences, facts about user, hates, likes, etc) to know user better over time, use long term memory tools` : ""}
8256
+ ${isMemoryEnabled ? `If user tell something that is important (like, hobbies, preferences, facts about user, hates, likes, etc) to know user better over time, use user memory tools` : ""}
7925
8257
 
7926
8258
  ${JANITOR_TOOLS_PROTOCOL(isMemoryEnabled, needTitle)}
7927
8259
  === END SYSTEM PROMPT ===${userMemories ? `
@@ -7933,35 +8265,35 @@ ${userMemories}` : ""}`.trim();
7933
8265
  });
7934
8266
 
7935
8267
  // src/utils/revert.js
7936
- import fs7 from "fs-extra";
7937
- import path6 from "path";
8268
+ import fs8 from "fs-extra";
8269
+ import path7 from "path";
7938
8270
  async function performRestoration(change, tx) {
7939
8271
  try {
7940
8272
  if (change.type === "create") {
7941
- if (await fs7.pathExists(change.filePath)) {
7942
- await fs7.chmod(change.filePath, 438).catch(() => {
8273
+ if (await fs8.pathExists(change.filePath)) {
8274
+ await fs8.chmod(change.filePath, 438).catch(() => {
7943
8275
  });
7944
- await fs7.remove(change.filePath);
8276
+ await fs8.remove(change.filePath);
7945
8277
  }
7946
8278
  } else if (change.type === "update") {
7947
8279
  if (!change.backupFile) return;
7948
- const backupPath = path6.join(BACKUPS_DIR, tx.chatId, change.backupFile);
7949
- if (await fs7.pathExists(backupPath)) {
8280
+ const backupPath = path7.join(BACKUPS_DIR, tx.chatId, change.backupFile);
8281
+ if (await fs8.pathExists(backupPath)) {
7950
8282
  const backupContainer = readEncryptedJson(backupPath, null);
7951
8283
  if (!backupContainer || !backupContainer.data) {
7952
- throw new Error(`Backup container corrupt or empty for ${path6.basename(change.filePath)}`);
8284
+ throw new Error(`Backup container corrupt or empty for ${path7.basename(change.filePath)}`);
7953
8285
  }
7954
8286
  const decrypted = decryptAes(backupContainer.data);
7955
- if (await fs7.pathExists(change.filePath)) {
7956
- await fs7.chmod(change.filePath, 438).catch(() => {
8287
+ if (await fs8.pathExists(change.filePath)) {
8288
+ await fs8.chmod(change.filePath, 438).catch(() => {
7957
8289
  });
7958
8290
  }
7959
- await fs7.writeFile(change.filePath, decrypted, "utf8");
8291
+ await fs8.writeFile(change.filePath, decrypted, "utf8");
7960
8292
  } else {
7961
8293
  }
7962
8294
  }
7963
8295
  } catch (err) {
7964
- throw new Error(`Restoration failed for ${path6.basename(change.filePath)}: ${err.message}`);
8296
+ throw new Error(`Restoration failed for ${path7.basename(change.filePath)}: ${err.message}`);
7965
8297
  }
7966
8298
  }
7967
8299
  async function restoreWithRetry(change, tx, maxAttempts = 7) {
@@ -7986,7 +8318,7 @@ var init_revert = __esm({
7986
8318
  "src/utils/revert.js"() {
7987
8319
  init_paths();
7988
8320
  init_crypto();
7989
- fs7.ensureDirSync(BACKUPS_DIR);
8321
+ fs8.ensureDirSync(BACKUPS_DIR);
7990
8322
  currentTransaction = null;
7991
8323
  lastChatId = null;
7992
8324
  RevertManager = {
@@ -8011,16 +8343,16 @@ var init_revert = __esm({
8011
8343
  if (lastTx) {
8012
8344
  const alreadyBackedUp2 = lastTx.changes.some((c) => c.filePath === absolutePath);
8013
8345
  if (alreadyBackedUp2) return;
8014
- const fileExists2 = await fs7.pathExists(absolutePath);
8346
+ const fileExists2 = await fs8.pathExists(absolutePath);
8015
8347
  let type2 = fileExists2 || forcedContent ? "update" : "create";
8016
8348
  let backupFile2 = null;
8017
8349
  if (type2 === "update") {
8018
- const fileName = path6.basename(absolutePath);
8350
+ const fileName = path7.basename(absolutePath);
8019
8351
  backupFile2 = `${lastTx.id}_${fileName}.bak`;
8020
- const chatBackupDir = path6.join(BACKUPS_DIR, lastTx.chatId);
8021
- await fs7.ensureDir(chatBackupDir);
8022
- const backupPath = path6.join(chatBackupDir, backupFile2);
8023
- let content = forcedContent !== null ? forcedContent : await fs7.readFile(absolutePath, "utf8").catch(() => null);
8352
+ const chatBackupDir = path7.join(BACKUPS_DIR, lastTx.chatId);
8353
+ await fs8.ensureDir(chatBackupDir);
8354
+ const backupPath = path7.join(chatBackupDir, backupFile2);
8355
+ let content = forcedContent !== null ? forcedContent : await fs8.readFile(absolutePath, "utf8").catch(() => null);
8024
8356
  if (content !== null) {
8025
8357
  writeEncryptedJson(backupPath, { data: encryptAes(content) });
8026
8358
  } else {
@@ -8036,16 +8368,16 @@ var init_revert = __esm({
8036
8368
  }
8037
8369
  const alreadyBackedUp = currentTransaction.changes.some((c) => c.filePath === absolutePath);
8038
8370
  if (alreadyBackedUp) return;
8039
- const fileExists = await fs7.pathExists(absolutePath);
8371
+ const fileExists = await fs8.pathExists(absolutePath);
8040
8372
  let type = fileExists || forcedContent ? "update" : "create";
8041
8373
  let backupFile = null;
8042
8374
  if (type === "update") {
8043
- const fileName = path6.basename(absolutePath);
8375
+ const fileName = path7.basename(absolutePath);
8044
8376
  backupFile = `${currentTransaction.id}_${fileName}.bak`;
8045
- const chatBackupDir = path6.join(BACKUPS_DIR, currentTransaction.chatId);
8046
- await fs7.ensureDir(chatBackupDir);
8047
- const backupPath = path6.join(chatBackupDir, backupFile);
8048
- let content = forcedContent !== null ? forcedContent : await fs7.readFile(absolutePath, "utf8").catch(() => null);
8377
+ const chatBackupDir = path7.join(BACKUPS_DIR, currentTransaction.chatId);
8378
+ await fs8.ensureDir(chatBackupDir);
8379
+ const backupPath = path7.join(chatBackupDir, backupFile);
8380
+ let content = forcedContent !== null ? forcedContent : await fs8.readFile(absolutePath, "utf8").catch(() => null);
8049
8381
  if (content !== null) {
8050
8382
  writeEncryptedJson(backupPath, { data: encryptAes(content) });
8051
8383
  } else {
@@ -8068,14 +8400,14 @@ var init_revert = __esm({
8068
8400
  if (removed.changes) {
8069
8401
  for (const change of removed.changes) {
8070
8402
  if (change.backupFile) {
8071
- await fs7.remove(path6.join(BACKUPS_DIR, removed.chatId, change.backupFile)).catch(() => {
8403
+ await fs8.remove(path7.join(BACKUPS_DIR, removed.chatId, change.backupFile)).catch(() => {
8072
8404
  });
8073
8405
  }
8074
8406
  }
8075
8407
  }
8076
8408
  }
8077
8409
  writeEncryptedJson(LEDGER_FILE, ledger);
8078
- await fs7.remove(ACTIVE_TX_FILE).catch(() => {
8410
+ await fs8.remove(ACTIVE_TX_FILE).catch(() => {
8079
8411
  });
8080
8412
  } catch (err) {
8081
8413
  } finally {
@@ -8084,7 +8416,7 @@ var init_revert = __esm({
8084
8416
  },
8085
8417
  async recoverCrashedTransaction() {
8086
8418
  try {
8087
- if (await fs7.pathExists(ACTIVE_TX_FILE)) {
8419
+ if (await fs8.pathExists(ACTIVE_TX_FILE)) {
8088
8420
  const orphanedTx = readEncryptedJson(ACTIVE_TX_FILE, null);
8089
8421
  if (orphanedTx?.changes?.length > 0) {
8090
8422
  const ledger = readEncryptedJson(LEDGER_FILE, []);
@@ -8093,7 +8425,7 @@ var init_revert = __esm({
8093
8425
  writeEncryptedJson(LEDGER_FILE, ledger);
8094
8426
  }
8095
8427
  }
8096
- await fs7.remove(ACTIVE_TX_FILE).catch(() => {
8428
+ await fs8.remove(ACTIVE_TX_FILE).catch(() => {
8097
8429
  });
8098
8430
  }
8099
8431
  } catch (e) {
@@ -8113,8 +8445,8 @@ var init_revert = __esm({
8113
8445
  }
8114
8446
  for (const change of tx.changes) {
8115
8447
  if (change.backupFile) {
8116
- const backupPath = path6.join(BACKUPS_DIR, tx.chatId, change.backupFile);
8117
- await fs7.remove(backupPath).catch(() => {
8448
+ const backupPath = path7.join(BACKUPS_DIR, tx.chatId, change.backupFile);
8449
+ await fs8.remove(backupPath).catch(() => {
8118
8450
  });
8119
8451
  }
8120
8452
  }
@@ -8133,7 +8465,7 @@ var init_revert = __esm({
8133
8465
  },
8134
8466
  async deleteChatBackups(chatId) {
8135
8467
  try {
8136
- await fs7.remove(path6.join(BACKUPS_DIR, chatId));
8468
+ await fs8.remove(path7.join(BACKUPS_DIR, chatId));
8137
8469
  let ledger = readEncryptedJson(LEDGER_FILE, []);
8138
8470
  const clean = ledger.filter((t) => t.chatId !== chatId);
8139
8471
  if (ledger.length !== clean.length) writeEncryptedJson(LEDGER_FILE, clean);
@@ -8145,8 +8477,8 @@ var init_revert = __esm({
8145
8477
  });
8146
8478
 
8147
8479
  // src/utils/history.js
8148
- import fs8 from "fs-extra";
8149
- import path7 from "path";
8480
+ import fs9 from "fs-extra";
8481
+ import path8 from "path";
8150
8482
  import { nanoid } from "nanoid";
8151
8483
  var WRITE_LOCK, withLock, loadHistory, saveChat, saveChatTitle, deleteChat, generateChatId, cleanupOldHistory, parseCustomDate, cleanupLogFile, cleanupOldLogs, getTruncatedHistory, saveChatContext, loadChatContext;
8152
8484
  var init_history = __esm({
@@ -8170,9 +8502,9 @@ var init_history = __esm({
8170
8502
  return nextLock;
8171
8503
  };
8172
8504
  loadHistory = async () => {
8173
- await fs8.ensureDir(HISTORY_DIR);
8505
+ await fs9.ensureDir(HISTORY_DIR);
8174
8506
  let history = {};
8175
- if (await fs8.pathExists(HISTORY_FILE)) {
8507
+ if (await fs9.pathExists(HISTORY_FILE)) {
8176
8508
  try {
8177
8509
  history = readEncryptedJson(HISTORY_FILE, {});
8178
8510
  } catch (e) {
@@ -8180,10 +8512,10 @@ var init_history = __esm({
8180
8512
  }
8181
8513
  }
8182
8514
  for (const id in history) {
8183
- const chatFile = path7.join(HISTORY_DIR, `${id}.json`);
8515
+ const chatFile = path8.join(HISTORY_DIR, `${id}.json`);
8184
8516
  Object.defineProperty(history[id], "messages", {
8185
8517
  get: () => {
8186
- if (fs8.existsSync(chatFile)) {
8518
+ if (fs9.existsSync(chatFile)) {
8187
8519
  try {
8188
8520
  return readEncryptedJson(chatFile, []);
8189
8521
  } catch (e) {
@@ -8206,7 +8538,7 @@ var init_history = __esm({
8206
8538
  };
8207
8539
  saveChat = async (id, name, messages) => {
8208
8540
  return withLock(async () => {
8209
- await fs8.ensureDir(HISTORY_DIR);
8541
+ await fs9.ensureDir(HISTORY_DIR);
8210
8542
  const history = await loadHistory();
8211
8543
  const existingChat = history[id];
8212
8544
  let persistentMessages = (messages || []).filter(
@@ -8238,7 +8570,7 @@ var init_history = __esm({
8238
8570
  const firstUserMsg = userMessages[0];
8239
8571
  const latestUserMsg = userMessages[userMessages.length - 1];
8240
8572
  if (existingChat && existingChat.prompt) {
8241
- if (Math.random() < 0.8) {
8573
+ if (Math.random() < 0.95) {
8242
8574
  prompt = extractPrompt(latestUserMsg) || existingChat.prompt;
8243
8575
  } else {
8244
8576
  prompt = existingChat.prompt;
@@ -8247,7 +8579,7 @@ var init_history = __esm({
8247
8579
  prompt = extractPrompt(firstUserMsg);
8248
8580
  }
8249
8581
  const finalName = name || (existingChat ? existingChat.name : prompt || `Session ${id.slice(-6)}`);
8250
- const chatFile = path7.join(HISTORY_DIR, `${id}.json`);
8582
+ const chatFile = path8.join(HISTORY_DIR, `${id}.json`);
8251
8583
  writeEncryptedJson(chatFile, persistentMessages);
8252
8584
  history[id] = {
8253
8585
  name: finalName,
@@ -8298,7 +8630,7 @@ var init_history = __esm({
8298
8630
  };
8299
8631
  }
8300
8632
  writeEncryptedJson(HISTORY_FILE, indexHistory);
8301
- if (await fs8.pathExists(CONTEXT_FILE)) {
8633
+ if (await fs9.pathExists(CONTEXT_FILE)) {
8302
8634
  try {
8303
8635
  const contextData = readEncryptedJson(CONTEXT_FILE, []);
8304
8636
  if (Array.isArray(contextData)) {
@@ -8319,10 +8651,10 @@ var init_history = __esm({
8319
8651
  writeEncryptedJson(TEMP_MEM_CHAT_FILE, cache);
8320
8652
  }
8321
8653
  await RevertManager.deleteChatBackups(id);
8322
- const chatFile = path7.join(HISTORY_DIR, `${id}.json`);
8323
- if (await fs8.pathExists(chatFile)) {
8654
+ const chatFile = path8.join(HISTORY_DIR, `${id}.json`);
8655
+ if (await fs9.pathExists(chatFile)) {
8324
8656
  try {
8325
- await fs8.remove(chatFile);
8657
+ await fs9.remove(chatFile);
8326
8658
  } catch (e) {
8327
8659
  }
8328
8660
  }
@@ -8395,8 +8727,8 @@ var init_history = __esm({
8395
8727
  };
8396
8728
  cleanupLogFile = async (filePath) => {
8397
8729
  try {
8398
- if (!await fs8.pathExists(filePath)) return;
8399
- const content = await fs8.readFile(filePath, "utf8");
8730
+ if (!await fs9.pathExists(filePath)) return;
8731
+ const content = await fs9.readFile(filePath, "utf8");
8400
8732
  if (!content.trim()) return;
8401
8733
  const lines = content.split("\n");
8402
8734
  const entries = [];
@@ -8436,26 +8768,26 @@ var init_history = __esm({
8436
8768
  }
8437
8769
  const finalContent = keptEntries.join("\n").trim();
8438
8770
  if (finalContent) {
8439
- await fs8.writeFile(filePath, finalContent + "\n", "utf8");
8771
+ await fs9.writeFile(filePath, finalContent + "\n", "utf8");
8440
8772
  } else {
8441
- await fs8.writeFile(filePath, "", "utf8");
8773
+ await fs9.writeFile(filePath, "", "utf8");
8442
8774
  }
8443
8775
  } catch (e) {
8444
8776
  }
8445
8777
  };
8446
8778
  cleanupOldLogs = async (logsDir) => {
8447
8779
  try {
8448
- if (!await fs8.pathExists(logsDir)) return;
8780
+ if (!await fs9.pathExists(logsDir)) return;
8449
8781
  const cleanRecursive = async (dir) => {
8450
- const files = await fs8.readdir(dir);
8782
+ const files = await fs9.readdir(dir);
8451
8783
  for (const file of files) {
8452
- const fullPath = path7.join(dir, file);
8453
- const stat = await fs8.stat(fullPath);
8784
+ const fullPath = path8.join(dir, file);
8785
+ const stat = await fs9.stat(fullPath);
8454
8786
  if (stat.isDirectory()) {
8455
8787
  await cleanRecursive(fullPath);
8456
- const subFiles = await fs8.readdir(fullPath);
8788
+ const subFiles = await fs9.readdir(fullPath);
8457
8789
  if (subFiles.length === 0) {
8458
- await fs8.remove(fullPath);
8790
+ await fs9.remove(fullPath);
8459
8791
  }
8460
8792
  } else if (file.endsWith(".log")) {
8461
8793
  await cleanupLogFile(fullPath);
@@ -8490,7 +8822,7 @@ var init_history = __esm({
8490
8822
  };
8491
8823
  loadChatContext = async (chatId) => {
8492
8824
  try {
8493
- if (!await fs8.pathExists(CONTEXT_FILE)) return { total: 0, context: 0 };
8825
+ if (!await fs9.pathExists(CONTEXT_FILE)) return { total: 0, context: 0 };
8494
8826
  const contextData = readEncryptedJson(CONTEXT_FILE, []);
8495
8827
  if (!Array.isArray(contextData)) return { total: 0, context: 0 };
8496
8828
  const entry = contextData.find((item) => Object.keys(item)[0] === String(chatId));
@@ -8503,8 +8835,8 @@ var init_history = __esm({
8503
8835
  });
8504
8836
 
8505
8837
  // src/utils/usage.js
8506
- import fs9 from "fs-extra";
8507
- import path8 from "path";
8838
+ import fs10 from "fs-extra";
8839
+ import path9 from "path";
8508
8840
  import os3 from "os";
8509
8841
  var getLocalBackupPath, BACKUP_FILE, generateSaveId, cachedUsage, writeTimeout, lastWriteTime, isDirty, defaultStats, purgeOldHistory, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, getMonthlyUsage, incrementUsage, runtimeSession, addToUsage, getCustomPeriodUsage, checkQuota, getImageQuotaBuckets, getImageQuotaLimit, checkImageQuota, getImageQuotaStats, recordImageGeneration;
8510
8842
  var init_usage = __esm({
@@ -8513,14 +8845,14 @@ var init_usage = __esm({
8513
8845
  init_crypto();
8514
8846
  getLocalBackupPath = () => {
8515
8847
  if (process.platform === "win32") {
8516
- const localAppData = process.env.LOCALAPPDATA || path8.join(os3.homedir(), "AppData", "Local");
8517
- return path8.join(localAppData, "FxFl", "backups", "backup.json");
8848
+ const localAppData = process.env.LOCALAPPDATA || path9.join(os3.homedir(), "AppData", "Local");
8849
+ return path9.join(localAppData, "FxFl", "backups", "backup.json");
8518
8850
  }
8519
8851
  if (process.platform === "darwin") {
8520
- return path8.join(os3.homedir(), "Library", "Application Support", "FxFl", "backups", "backup.json");
8852
+ return path9.join(os3.homedir(), "Library", "Application Support", "FxFl", "backups", "backup.json");
8521
8853
  }
8522
- const xdgDataHome = process.env.XDG_DATA_HOME || path8.join(os3.homedir(), ".local", "share");
8523
- return path8.join(xdgDataHome, "fxfl", "backups", "backup.json");
8854
+ const xdgDataHome = process.env.XDG_DATA_HOME || path9.join(os3.homedir(), ".local", "share");
8855
+ return path9.join(xdgDataHome, "fxfl", "backups", "backup.json");
8524
8856
  };
8525
8857
  BACKUP_FILE = getLocalBackupPath();
8526
8858
  generateSaveId = () => Math.random().toString(36).substring(2) + Date.now().toString(36);
@@ -8562,8 +8894,8 @@ var init_usage = __esm({
8562
8894
  let primaryData = null;
8563
8895
  let backupData = null;
8564
8896
  try {
8565
- if (await fs9.exists(tempFile)) {
8566
- const rawContent = (await fs9.readFile(tempFile, "utf8")).trim();
8897
+ if (await fs10.exists(tempFile)) {
8898
+ const rawContent = (await fs10.readFile(tempFile, "utf8")).trim();
8567
8899
  let parsed = null;
8568
8900
  if (rawContent.startsWith("{") || rawContent.startsWith("[")) {
8569
8901
  parsed = JSON.parse(rawContent);
@@ -8573,26 +8905,26 @@ var init_usage = __esm({
8573
8905
  if (parsed && parsed.date && parsed.stats) {
8574
8906
  primaryData = parsed;
8575
8907
  try {
8576
- await fs9.rename(tempFile, USAGE_FILE);
8908
+ await fs10.rename(tempFile, USAGE_FILE);
8577
8909
  } catch (e) {
8578
8910
  }
8579
8911
  } else {
8580
8912
  try {
8581
- await fs9.remove(tempFile);
8913
+ await fs10.remove(tempFile);
8582
8914
  } catch (e) {
8583
8915
  }
8584
8916
  }
8585
8917
  }
8586
8918
  } catch (err) {
8587
8919
  try {
8588
- await fs9.remove(tempFile);
8920
+ await fs10.remove(tempFile);
8589
8921
  } catch (e) {
8590
8922
  }
8591
8923
  }
8592
8924
  if (!primaryData) {
8593
8925
  try {
8594
- if (await fs9.exists(USAGE_FILE)) {
8595
- const rawContent = (await fs9.readFile(USAGE_FILE, "utf8")).trim();
8926
+ if (await fs10.exists(USAGE_FILE)) {
8927
+ const rawContent = (await fs10.readFile(USAGE_FILE, "utf8")).trim();
8596
8928
  if (rawContent.startsWith("{") || rawContent.startsWith("[")) {
8597
8929
  primaryData = JSON.parse(rawContent);
8598
8930
  } else {
@@ -8603,8 +8935,8 @@ var init_usage = __esm({
8603
8935
  }
8604
8936
  }
8605
8937
  try {
8606
- if (await fs9.exists(BACKUP_FILE)) {
8607
- const rawContent = (await fs9.readFile(BACKUP_FILE, "utf8")).trim();
8938
+ if (await fs10.exists(BACKUP_FILE)) {
8939
+ const rawContent = (await fs10.readFile(BACKUP_FILE, "utf8")).trim();
8608
8940
  if (rawContent.startsWith("{") || rawContent.startsWith("[")) {
8609
8941
  backupData = JSON.parse(rawContent);
8610
8942
  } else {
@@ -8618,8 +8950,8 @@ var init_usage = __esm({
8618
8950
  if (primaryData.saveId !== backupData.saveId) {
8619
8951
  resolvedData = primaryData;
8620
8952
  try {
8621
- await fs9.ensureDir(path8.dirname(BACKUP_FILE));
8622
- await fs9.copy(USAGE_FILE, BACKUP_FILE);
8953
+ await fs10.ensureDir(path9.dirname(BACKUP_FILE));
8954
+ await fs10.copy(USAGE_FILE, BACKUP_FILE);
8623
8955
  } catch (e) {
8624
8956
  }
8625
8957
  } else {
@@ -8628,15 +8960,15 @@ var init_usage = __esm({
8628
8960
  } else if (primaryData && !backupData) {
8629
8961
  resolvedData = primaryData;
8630
8962
  try {
8631
- await fs9.ensureDir(path8.dirname(BACKUP_FILE));
8632
- await fs9.copy(USAGE_FILE, BACKUP_FILE);
8963
+ await fs10.ensureDir(path9.dirname(BACKUP_FILE));
8964
+ await fs10.copy(USAGE_FILE, BACKUP_FILE);
8633
8965
  } catch (e) {
8634
8966
  }
8635
8967
  } else if (!primaryData && backupData) {
8636
8968
  resolvedData = backupData;
8637
8969
  try {
8638
- await fs9.ensureDir(path8.dirname(USAGE_FILE));
8639
- await fs9.copy(BACKUP_FILE, USAGE_FILE);
8970
+ await fs10.ensureDir(path9.dirname(USAGE_FILE));
8971
+ await fs10.copy(BACKUP_FILE, USAGE_FILE);
8640
8972
  } catch (e) {
8641
8973
  }
8642
8974
  }
@@ -8676,11 +9008,11 @@ var init_usage = __esm({
8676
9008
  flushUsage = async () => {
8677
9009
  if (!isDirty || !cachedUsage) return;
8678
9010
  try {
8679
- await fs9.ensureDir(path8.dirname(USAGE_FILE));
9011
+ await fs10.ensureDir(path9.dirname(USAGE_FILE));
8680
9012
  let diskData = null;
8681
9013
  try {
8682
- if (await fs9.exists(USAGE_FILE)) {
8683
- const rawContent = (await fs9.readFile(USAGE_FILE, "utf8")).trim();
9014
+ if (await fs10.exists(USAGE_FILE)) {
9015
+ const rawContent = (await fs10.readFile(USAGE_FILE, "utf8")).trim();
8684
9016
  if (rawContent.startsWith("{") || rawContent.startsWith("[")) {
8685
9017
  diskData = JSON.parse(rawContent);
8686
9018
  } else {
@@ -8756,14 +9088,14 @@ var init_usage = __esm({
8756
9088
  cachedUsage.saveId = generateSaveId();
8757
9089
  const tempFile = USAGE_FILE + ".tmp";
8758
9090
  const encryptedStr = encryptAes(JSON.stringify(cachedUsage, null, 2));
8759
- await fs9.writeFile(tempFile, encryptedStr, "utf8");
8760
- const fd = await fs9.open(tempFile, "r+");
8761
- await fs9.fsync(fd);
8762
- await fs9.close(fd);
8763
- await fs9.rename(tempFile, USAGE_FILE);
9091
+ await fs10.writeFile(tempFile, encryptedStr, "utf8");
9092
+ const fd = await fs10.open(tempFile, "r+");
9093
+ await fs10.fsync(fd);
9094
+ await fs10.close(fd);
9095
+ await fs10.rename(tempFile, USAGE_FILE);
8764
9096
  try {
8765
- await fs9.ensureDir(path8.dirname(BACKUP_FILE));
8766
- await fs9.copy(USAGE_FILE, BACKUP_FILE);
9097
+ await fs10.ensureDir(path9.dirname(BACKUP_FILE));
9098
+ await fs10.copy(USAGE_FILE, BACKUP_FILE);
8767
9099
  } catch (backupErr) {
8768
9100
  }
8769
9101
  isDirty = false;
@@ -9247,8 +9579,8 @@ var init_usage = __esm({
9247
9579
 
9248
9580
  // src/utils/puppeteer_helper.js
9249
9581
  import os4 from "os";
9250
- import path9 from "path";
9251
- import fs10 from "fs";
9582
+ import path10 from "path";
9583
+ import fs11 from "fs";
9252
9584
  import { createRequire } from "module";
9253
9585
  import { fileURLToPath as fileURLToPath2 } from "url";
9254
9586
  function getPuppeteerConfig() {
@@ -9273,11 +9605,11 @@ function getPuppeteerConfig() {
9273
9605
  } else {
9274
9606
  return {};
9275
9607
  }
9276
- let configPath = path9.resolve(__dirname2, "..", "..", ".puppeteerrc.cjs");
9277
- if (!fs10.existsSync(configPath)) {
9278
- configPath = path9.resolve(__dirname2, "..", ".puppeteerrc.cjs");
9608
+ let configPath = path10.resolve(__dirname2, "..", "..", ".puppeteerrc.cjs");
9609
+ if (!fs11.existsSync(configPath)) {
9610
+ configPath = path10.resolve(__dirname2, "..", ".puppeteerrc.cjs");
9279
9611
  }
9280
- if (!fs10.existsSync(configPath)) {
9612
+ if (!fs11.existsSync(configPath)) {
9281
9613
  return {};
9282
9614
  }
9283
9615
  try {
@@ -9287,14 +9619,14 @@ function getPuppeteerConfig() {
9287
9619
  if (cacheDir) {
9288
9620
  process.env.PUPPETEER_CACHE_DIR = cacheDir;
9289
9621
  if (version) {
9290
- const expectedPath = path9.join(
9622
+ const expectedPath = path10.join(
9291
9623
  cacheDir,
9292
9624
  "chrome",
9293
9625
  `${pptrPlatform}-${version}`,
9294
9626
  subDir,
9295
9627
  execName
9296
9628
  );
9297
- if (fs10.existsSync(expectedPath)) {
9629
+ if (fs11.existsSync(expectedPath)) {
9298
9630
  return {
9299
9631
  executablePath: expectedPath,
9300
9632
  cacheDirectory: cacheDir
@@ -9302,15 +9634,15 @@ function getPuppeteerConfig() {
9302
9634
  }
9303
9635
  }
9304
9636
  const findExecutable = (dir) => {
9305
- if (!fs10.existsSync(dir)) return null;
9637
+ if (!fs11.existsSync(dir)) return null;
9306
9638
  try {
9307
- const files = fs10.readdirSync(dir);
9639
+ const files = fs11.readdirSync(dir);
9308
9640
  const dirsToSearch = [];
9309
9641
  for (const file of files) {
9310
- const fullPath = path9.join(dir, file);
9642
+ const fullPath = path10.join(dir, file);
9311
9643
  let stat;
9312
9644
  try {
9313
- stat = fs10.statSync(fullPath);
9645
+ stat = fs11.statSync(fullPath);
9314
9646
  } catch (e) {
9315
9647
  continue;
9316
9648
  }
@@ -9356,14 +9688,14 @@ var require2, __dirname2;
9356
9688
  var init_puppeteer_helper = __esm({
9357
9689
  "src/utils/puppeteer_helper.js"() {
9358
9690
  require2 = createRequire(import.meta.url);
9359
- __dirname2 = path9.dirname(fileURLToPath2(import.meta.url));
9691
+ __dirname2 = path10.dirname(fileURLToPath2(import.meta.url));
9360
9692
  }
9361
9693
  });
9362
9694
 
9363
9695
  // src/tools/web_search.js
9364
9696
  import puppeteer from "puppeteer";
9365
- import fs11 from "fs";
9366
- import path10 from "path";
9697
+ import fs12 from "fs";
9698
+ import path11 from "path";
9367
9699
  var web_search;
9368
9700
  var init_web_search = __esm({
9369
9701
  "src/tools/web_search.js"() {
@@ -9512,7 +9844,7 @@ Sources:
9512
9844
  ${aiResult}`;
9513
9845
  } catch (err) {
9514
9846
  lastError = err;
9515
- fs11.writeFileSync(path10.join(LOGS_DIR, "web_tools", "search", "ai_mode", "ERROR.txt"), err.message);
9847
+ fs12.writeFileSync(path11.join(LOGS_DIR, "web_tools", "search", "ai_mode", "ERROR.txt"), err.message);
9516
9848
  if (browser) await browser.close();
9517
9849
  if (attempt < maxRetries) {
9518
9850
  const backoff = Math.pow(2, attempt) * 1e3;
@@ -9574,7 +9906,7 @@ ${finalResults}`;
9574
9906
  } catch (err) {
9575
9907
  lastError = err;
9576
9908
  if (browser) await browser.close();
9577
- fs11.writeFileSync(path10.join(LOGS_DIR, "web_tools", "search", "standard_mode", "ERROR.txt"), err.message);
9909
+ fs12.writeFileSync(path11.join(LOGS_DIR, "web_tools", "search", "standard_mode", "ERROR.txt"), err.message);
9578
9910
  if (attempt < maxRetries) {
9579
9911
  const backoff = Math.pow(2, attempt) * 1e3;
9580
9912
  await new Promise((r) => setTimeout(r, backoff));
@@ -9588,8 +9920,8 @@ ${finalResults}`;
9588
9920
 
9589
9921
  // src/tools/web_scrape.js
9590
9922
  import puppeteer2 from "puppeteer";
9591
- import fs12 from "fs";
9592
- import path11 from "path";
9923
+ import fs13 from "fs";
9924
+ import path12 from "path";
9593
9925
  var web_scrape;
9594
9926
  var init_web_scrape = __esm({
9595
9927
  "src/tools/web_scrape.js"() {
@@ -9666,7 +9998,7 @@ ${cleanedHtml}${htmlContent.length > 5e4 ? "\n\n[TRUNCATED AT 50K CHARS]" : ""}`
9666
9998
  } catch (err) {
9667
9999
  lastError = err;
9668
10000
  if (browser) await browser.close();
9669
- fs12.writeFileSync(path11.join(LOGS_DIR, "web_tools", "scrape", "standard_mode", "ERROR.txt"), err.message);
10001
+ fs13.writeFileSync(path12.join(LOGS_DIR, "web_tools", "scrape", "standard_mode", "ERROR.txt"), err.message);
9670
10002
  if (attempt < maxRetries) {
9671
10003
  const backoff = Math.pow(2, attempt) * 1e3;
9672
10004
  await new Promise((r) => setTimeout(r, backoff));
@@ -9790,8 +10122,8 @@ var init_chat = __esm({
9790
10122
  });
9791
10123
 
9792
10124
  // src/tools/view_file.js
9793
- import fs13 from "fs";
9794
- import path12 from "path";
10125
+ import fs14 from "fs";
10126
+ import path13 from "path";
9795
10127
  var view_file;
9796
10128
  var init_view_file = __esm({
9797
10129
  "src/tools/view_file.js"() {
@@ -9803,16 +10135,16 @@ var init_view_file = __esm({
9803
10135
  const finalStart = sLine || 1;
9804
10136
  const finalEnd = eLine || (sLine ? sLine + 800 : 800);
9805
10137
  if (!targetPath) return 'ERROR: Missing "path" argument for view_file.';
9806
- const absolutePath = path12.resolve(process.cwd(), targetPath);
10138
+ const absolutePath = path13.resolve(process.cwd(), targetPath);
9807
10139
  try {
9808
- if (!fs13.existsSync(absolutePath)) {
10140
+ if (!fs14.existsSync(absolutePath)) {
9809
10141
  return `ERROR: File [${targetPath}] does not exist.`;
9810
10142
  }
9811
- const stats = fs13.statSync(absolutePath);
10143
+ const stats = fs14.statSync(absolutePath);
9812
10144
  if (stats.isDirectory()) {
9813
10145
  return `ERROR: Path [${targetPath}] is a directory. Use list_files instead.`;
9814
10146
  }
9815
- const ext = path12.extname(targetPath).toLowerCase();
10147
+ const ext = path13.extname(targetPath).toLowerCase();
9816
10148
  const videoExtensions = [".mp4", ".mkv", ".avi", ".mov", ".webm", ".flv", ".wmv", ".mpeg", ".mpg"];
9817
10149
  if (videoExtensions.includes(ext)) {
9818
10150
  const format = ext.slice(1).toUpperCase();
@@ -9832,7 +10164,7 @@ var init_view_file = __esm({
9832
10164
  if (!isMultiModal) {
9833
10165
  return `ERROR: Multimodality is not supported for the current model. Unable to load [${targetPath}].`;
9834
10166
  }
9835
- const buffer = fs13.readFileSync(absolutePath);
10167
+ const buffer = fs14.readFileSync(absolutePath);
9836
10168
  const base64 = buffer.toString("base64");
9837
10169
  const mimeType = mimeMap[ext];
9838
10170
  return {
@@ -9845,7 +10177,7 @@ var init_view_file = __esm({
9845
10177
  }
9846
10178
  };
9847
10179
  }
9848
- let content = fs13.readFileSync(absolutePath, "utf8");
10180
+ let content = fs14.readFileSync(absolutePath, "utf8");
9849
10181
  if (content.startsWith("\uFEFF")) {
9850
10182
  content = content.slice(1);
9851
10183
  }
@@ -9869,8 +10201,8 @@ ${code}`;
9869
10201
  });
9870
10202
 
9871
10203
  // src/tools/write_file.js
9872
- import fs14 from "fs";
9873
- import path13 from "path";
10204
+ import fs15 from "fs";
10205
+ import path14 from "path";
9874
10206
  var write_file;
9875
10207
  var init_write_file = __esm({
9876
10208
  "src/tools/write_file.js"() {
@@ -9881,14 +10213,14 @@ var init_write_file = __esm({
9881
10213
  if (!targetPath) return 'ERROR: Missing "path" argument for write_file.';
9882
10214
  if (content === void 0) return 'ERROR: Missing "content" argument for write_file.';
9883
10215
  content = content.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
9884
- const absolutePath = path13.resolve(process.cwd(), targetPath);
9885
- const parentDir = path13.dirname(absolutePath);
10216
+ const absolutePath = path14.resolve(process.cwd(), targetPath);
10217
+ const parentDir = path14.dirname(absolutePath);
9886
10218
  try {
9887
10219
  await RevertManager.recordFileChange(absolutePath);
9888
10220
  let ancestry = "";
9889
- if (fs14.existsSync(absolutePath)) {
10221
+ if (fs15.existsSync(absolutePath)) {
9890
10222
  try {
9891
- const oldData = fs14.readFileSync(absolutePath, "utf8");
10223
+ const oldData = fs15.readFileSync(absolutePath, "utf8");
9892
10224
  const lines = oldData.split(/\r?\n/);
9893
10225
  ancestry = `Old File contents:
9894
10226
  ${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
@@ -9900,16 +10232,16 @@ ${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
9900
10232
  `;
9901
10233
  }
9902
10234
  }
9903
- if (!fs14.existsSync(parentDir)) {
9904
- fs14.mkdirSync(parentDir, { recursive: true });
10235
+ if (!fs15.existsSync(parentDir)) {
10236
+ fs15.mkdirSync(parentDir, { recursive: true });
9905
10237
  }
9906
10238
  const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
9907
10239
  const processedContent = strip(content);
9908
10240
  const finalContent = processedContent.endsWith("\n") ? processedContent : processedContent + "\n";
9909
10241
  const lineCount = finalContent.split(/\r?\n/).length;
9910
10242
  const originalSize = Buffer.byteLength(finalContent, "utf8");
9911
- fs14.writeFileSync(absolutePath, finalContent, "utf8");
9912
- let verifiedContent = fs14.readFileSync(absolutePath, "utf8");
10243
+ fs15.writeFileSync(absolutePath, finalContent, "utf8");
10244
+ let verifiedContent = fs15.readFileSync(absolutePath, "utf8");
9913
10245
  const verifiedSize = Buffer.byteLength(verifiedContent, "utf8");
9914
10246
  const verifiedLines = verifiedContent.split(/\r?\n/);
9915
10247
  const verifiedLineCount = verifiedLines.length;
@@ -9944,8 +10276,8 @@ ${snippet}`;
9944
10276
  });
9945
10277
 
9946
10278
  // src/tools/update_file.js
9947
- import fs15 from "fs";
9948
- import path14 from "path";
10279
+ import fs16 from "fs";
10280
+ import path15 from "path";
9949
10281
  var update_file;
9950
10282
  var init_update_file = __esm({
9951
10283
  "src/tools/update_file.js"() {
@@ -9956,20 +10288,21 @@ var init_update_file = __esm({
9956
10288
  const parsed = parseArgs(args);
9957
10289
  const targetPath = parsed.path;
9958
10290
  if (!targetPath) return 'ERROR: Missing "path" argument for update_file.';
9959
- const { patchPairs, error: parseError } = parsePatchPairs(parsed);
10291
+ const { patchPairs, allowMultiple: parsedAllowMultiple, error: parseError } = parsePatchPairs(parsed);
9960
10292
  if (parseError) return `ERROR: ${parseError}`;
9961
10293
  if (patchPairs.length === 0) {
9962
10294
  return "ERROR: No valid replacement pairs found. Use replaceContent1, newContent1, etc.";
9963
10295
  }
9964
- const absolutePath = path14.resolve(process.cwd(), targetPath);
10296
+ const allowMultiple = parsed.allowMultiple !== void 0 ? parsed.allowMultiple === true || String(parsed.allowMultiple).toLowerCase() === "true" : parsedAllowMultiple;
10297
+ const absolutePath = path15.resolve(process.cwd(), targetPath);
9965
10298
  try {
9966
- if (!fs15.existsSync(absolutePath)) {
10299
+ if (!fs16.existsSync(absolutePath)) {
9967
10300
  return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
9968
10301
  }
9969
- let diskContent = context.forcedContent || fs15.readFileSync(absolutePath, "utf8");
10302
+ let diskContent = context.forcedContent || fs16.readFileSync(absolutePath, "utf8");
9970
10303
  if (diskContent.startsWith("\uFEFF")) diskContent = diskContent.slice(1);
9971
10304
  const originalContent = diskContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
9972
- const { content: finalContent, results } = applyPatches(originalContent, patchPairs);
10305
+ const { content: finalContent, results } = applyPatches(originalContent, patchPairs, { allowMultiple });
9973
10306
  const failures = results.filter((r) => !r.success);
9974
10307
  const successes = results.filter((r) => r.success);
9975
10308
  if (successes.length === 0) {
@@ -9977,7 +10310,7 @@ var init_update_file = __esm({
9977
10310
  ${failures.map((f) => ` \u2022 ${f.error}`).join("\n")}`;
9978
10311
  }
9979
10312
  await RevertManager.recordFileChange(absolutePath, originalContent);
9980
- fs15.writeFileSync(absolutePath, finalContent, "utf8");
10313
+ fs16.writeFileSync(absolutePath, finalContent, "utf8");
9981
10314
  const diffText = generateHighFidelityDiff(originalContent, finalContent, results, 12);
9982
10315
  if (failures.length > 0) {
9983
10316
  return `SUCCESS: File [${targetPath}] updated with some blocks failed. [${successes.length}/${patchPairs.length}] blocks applied.
@@ -9999,34 +10332,34 @@ ${diffText}`;
9999
10332
  });
10000
10333
 
10001
10334
  // src/tools/read_folder.js
10002
- import fs16 from "fs";
10003
- import path15 from "path";
10335
+ import fs17 from "fs";
10336
+ import path16 from "path";
10004
10337
  var read_folder;
10005
10338
  var init_read_folder = __esm({
10006
10339
  "src/tools/read_folder.js"() {
10007
10340
  init_arg_parser();
10008
10341
  read_folder = async (args) => {
10009
10342
  const { path: targetPath = "." } = parseArgs(args);
10010
- const absolutePath = path15.resolve(process.cwd(), targetPath);
10343
+ const absolutePath = path16.resolve(process.cwd(), targetPath);
10011
10344
  try {
10012
- if (!fs16.existsSync(absolutePath)) {
10345
+ if (!fs17.existsSync(absolutePath)) {
10013
10346
  return `ERROR: Path [${targetPath}] does not exist.`;
10014
10347
  }
10015
- const stats = fs16.statSync(absolutePath);
10348
+ const stats = fs17.statSync(absolutePath);
10016
10349
  if (!stats.isDirectory()) {
10017
10350
  return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
10018
10351
  }
10019
- const files = fs16.readdirSync(absolutePath);
10352
+ const files = fs17.readdirSync(absolutePath);
10020
10353
  const totalItems = files.length;
10021
10354
  const maxDisplay = 100;
10022
10355
  const displayItems = files.slice(0, maxDisplay);
10023
10356
  const folderData = [];
10024
10357
  for (const file of displayItems) {
10025
- const fPath = path15.join(absolutePath, file);
10358
+ const fPath = path16.join(absolutePath, file);
10026
10359
  let indicator = "\u{1F4C4}";
10027
10360
  let info = { name: file, type: "unknown", size: "N/A", mtime: "N/A" };
10028
10361
  try {
10029
- const fStats = fs16.statSync(fPath);
10362
+ const fStats = fs17.statSync(fPath);
10030
10363
  info = {
10031
10364
  name: file,
10032
10365
  type: fStats.isDirectory() ? "directory" : "file",
@@ -10111,8 +10444,8 @@ var init_ask_user = __esm({
10111
10444
 
10112
10445
  // src/tools/write_pdf.js
10113
10446
  import puppeteer3 from "puppeteer";
10114
- import path16 from "path";
10115
- import fs17 from "fs-extra";
10447
+ import path17 from "path";
10448
+ import fs18 from "fs-extra";
10116
10449
  import { PDFDocument } from "pdf-lib";
10117
10450
  var write_pdf;
10118
10451
  var init_write_pdf = __esm({
@@ -10129,10 +10462,10 @@ var init_write_pdf = __esm({
10129
10462
  } = parseArgs(args);
10130
10463
  if (!targetPath) return 'ERROR: Missing "path" argument for write_pdf.';
10131
10464
  if (!content) return 'ERROR: Missing "content" (HTML/CSS) for write_pdf.';
10132
- const absolutePath = path16.resolve(process.cwd(), targetPath);
10465
+ const absolutePath = path17.resolve(process.cwd(), targetPath);
10133
10466
  let browser = null;
10134
10467
  try {
10135
- await fs17.ensureDir(path16.dirname(absolutePath));
10468
+ await fs18.ensureDir(path17.dirname(absolutePath));
10136
10469
  await RevertManager.recordFileChange(absolutePath);
10137
10470
  const pptrConfig = getPuppeteerConfig();
10138
10471
  browser = await puppeteer3.launch({
@@ -10153,11 +10486,11 @@ var init_write_pdf = __esm({
10153
10486
  return null;
10154
10487
  }
10155
10488
  try {
10156
- const imgPath = path16.resolve(process.cwd(), originalSrc);
10157
- if (await fs17.pathExists(imgPath)) {
10158
- const ext = path16.extname(imgPath).toLowerCase().replace(".", "") || "png";
10489
+ const imgPath = path17.resolve(process.cwd(), originalSrc);
10490
+ if (await fs18.pathExists(imgPath)) {
10491
+ const ext = path17.extname(imgPath).toLowerCase().replace(".", "") || "png";
10159
10492
  const mime = ext === "jpg" ? "jpeg" : ext === "svg" ? "svg+xml" : ext;
10160
- const base64 = await fs17.readFile(imgPath, "base64");
10493
+ const base64 = await fs18.readFile(imgPath, "base64");
10161
10494
  return `data:image/${mime};base64,${base64}`;
10162
10495
  }
10163
10496
  } catch (e) {
@@ -10172,9 +10505,9 @@ var init_write_pdf = __esm({
10172
10505
  const fullTag = match[0];
10173
10506
  if (originalHref && fullTag.toLowerCase().includes("stylesheet") && !originalHref.startsWith("http://") && !originalHref.startsWith("https://") && !originalHref.startsWith("data:")) {
10174
10507
  try {
10175
- const cssPath = path16.resolve(process.cwd(), originalHref);
10176
- if (await fs17.pathExists(cssPath)) {
10177
- const cssContent = await fs17.readFile(cssPath, "utf-8");
10508
+ const cssPath = path17.resolve(process.cwd(), originalHref);
10509
+ if (await fs18.pathExists(cssPath)) {
10510
+ const cssContent = await fs18.readFile(cssPath, "utf-8");
10178
10511
  cssCache[fullTag] = `<style>${cssContent}</style>`;
10179
10512
  }
10180
10513
  } catch (e) {
@@ -10255,7 +10588,7 @@ var init_write_pdf = __esm({
10255
10588
  printBackground: true
10256
10589
  });
10257
10590
  const pdfDoc = await PDFDocument.load(pdfBytes);
10258
- const fileName = path16.basename(targetPath);
10591
+ const fileName = path17.basename(targetPath);
10259
10592
  pdfDoc.setTitle(`FluxFlow_${fileName}`);
10260
10593
  pdfDoc.setAuthor("FluxFlow CLI");
10261
10594
  pdfDoc.setSubject("Generated with Agentic AI System");
@@ -10263,8 +10596,8 @@ var init_write_pdf = __esm({
10263
10596
  pdfDoc.setCreator("FluxFlow PDF Engine");
10264
10597
  pdfDoc.setProducer("FluxFlow (Generative AI)");
10265
10598
  const finalPdfBytes = await pdfDoc.save();
10266
- await fs17.writeFile(absolutePath, finalPdfBytes);
10267
- const stats = await fs17.stat(absolutePath);
10599
+ await fs18.writeFile(absolutePath, finalPdfBytes);
10600
+ const stats = await fs18.stat(absolutePath);
10268
10601
  return `SUCCESS: PDF generated successfully at [${targetPath}] (${(stats.size / 1024).toFixed(2)} KB).`;
10269
10602
  } catch (err) {
10270
10603
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -10277,8 +10610,8 @@ var init_write_pdf = __esm({
10277
10610
  });
10278
10611
 
10279
10612
  // src/tools/write_docx.js
10280
- import fs18 from "fs-extra";
10281
- import path17 from "path";
10613
+ import fs19 from "fs-extra";
10614
+ import path18 from "path";
10282
10615
  import HTMLtoDOCX from "html-to-docx";
10283
10616
  var write_docx;
10284
10617
  var init_write_docx = __esm({
@@ -10292,11 +10625,11 @@ var init_write_docx = __esm({
10292
10625
  } = parseArgs(args);
10293
10626
  if (!targetPath) return 'ERROR: Missing "path" argument for write_docx.';
10294
10627
  if (!content) return 'ERROR: Missing "content" (HTML) for write_docx.';
10295
- const absolutePath = path17.resolve(process.cwd(), targetPath);
10628
+ const absolutePath = path18.resolve(process.cwd(), targetPath);
10296
10629
  try {
10297
- await fs18.ensureDir(path17.dirname(absolutePath));
10630
+ await fs19.ensureDir(path18.dirname(absolutePath));
10298
10631
  await RevertManager.recordFileChange(absolutePath);
10299
- const fileName = path17.basename(targetPath);
10632
+ const fileName = path18.basename(targetPath);
10300
10633
  const fullHtml = content.includes("<html") ? content : `
10301
10634
  <!DOCTYPE html>
10302
10635
  <html lang="en">
@@ -10317,7 +10650,7 @@ var init_write_docx = __esm({
10317
10650
  footer: true,
10318
10651
  pageNumber: true
10319
10652
  });
10320
- await fs18.writeFile(absolutePath, docxBuffer);
10653
+ await fs19.writeFile(absolutePath, docxBuffer);
10321
10654
  return `SUCCESS: Word document [${targetPath}] generated successfully.
10322
10655
  - Size: ${(docxBuffer.length / 1024).toFixed(1)} KB`;
10323
10656
  } catch (err) {
@@ -10329,21 +10662,21 @@ var init_write_docx = __esm({
10329
10662
  });
10330
10663
 
10331
10664
  // src/tools/search_keyword.js
10332
- import fs19 from "fs/promises";
10333
- import path18 from "path";
10665
+ import fs20 from "fs/promises";
10666
+ import path19 from "path";
10334
10667
  async function getFilesRecursively(dir, excludes, baseDir = dir, depth = 1) {
10335
10668
  if (depth > 12) return [];
10336
10669
  let results = [];
10337
10670
  let list;
10338
10671
  try {
10339
- list = await fs19.readdir(dir, { withFileTypes: true });
10672
+ list = await fs20.readdir(dir, { withFileTypes: true });
10340
10673
  } catch {
10341
10674
  return [];
10342
10675
  }
10343
10676
  for (const file of list) {
10344
- const fullPath = path18.join(dir, file.name);
10345
- const relativePath = path18.relative(baseDir, fullPath);
10346
- const pathSegments = relativePath.split(path18.sep).map((s) => s.toLowerCase());
10677
+ const fullPath = path19.join(dir, file.name);
10678
+ const relativePath = path19.relative(baseDir, fullPath);
10679
+ const pathSegments = relativePath.split(path19.sep).map((s) => s.toLowerCase());
10347
10680
  const isExcluded = excludes.some((ex) => pathSegments.includes(ex.toLowerCase()));
10348
10681
  if (isExcluded) continue;
10349
10682
  if (file.isDirectory()) {
@@ -10444,15 +10777,15 @@ var init_search_keyword = __esm({
10444
10777
  let pathArgType = null;
10445
10778
  if (pathArg) {
10446
10779
  const normalised = pathArg.replace(/[\/\\]+$/, "");
10447
- const fullPath = path18.resolve(rootDir, normalised);
10780
+ const fullPath = path19.resolve(rootDir, normalised);
10448
10781
  try {
10449
- const stat = await fs19.stat(fullPath);
10782
+ const stat = await fs20.stat(fullPath);
10450
10783
  if (stat.isDirectory()) {
10451
10784
  pathArgType = "dir";
10452
10785
  filesToSearch = await getFilesRecursively(fullPath, excludes, rootDir);
10453
10786
  } else if (stat.isFile()) {
10454
10787
  pathArgType = "file";
10455
- filesToSearch.push({ fullPath, relativePath: path18.relative(rootDir, fullPath) });
10788
+ filesToSearch.push({ fullPath, relativePath: path19.relative(rootDir, fullPath) });
10456
10789
  } else {
10457
10790
  return `ERROR: Path is neither a file nor a directory: ${pathArg}`;
10458
10791
  }
@@ -10464,7 +10797,7 @@ var init_search_keyword = __esm({
10464
10797
  }
10465
10798
  const searchPromises = filesToSearch.map(async (fileObj) => {
10466
10799
  try {
10467
- const content = await fs19.readFile(fileObj.fullPath, "utf-8");
10800
+ const content = await fs20.readFile(fileObj.fullPath, "utf-8");
10468
10801
  if (content.includes("\0")) return [];
10469
10802
  const lines = content.split(/\r?\n/);
10470
10803
  const fileMatches = [];
@@ -10536,8 +10869,8 @@ var init_search_keyword = __esm({
10536
10869
  });
10537
10870
 
10538
10871
  // src/tools/generate_image.js
10539
- import fs20 from "fs-extra";
10540
- import path19 from "path";
10872
+ import fs21 from "fs-extra";
10873
+ import path20 from "path";
10541
10874
  var injectPngMetadata, generate_image;
10542
10875
  var init_generate_image = __esm({
10543
10876
  "src/tools/generate_image.js"() {
@@ -10716,12 +11049,12 @@ var init_generate_image = __esm({
10716
11049
  "Seed": String(seed)
10717
11050
  };
10718
11051
  finalBuffer = injectPngMetadata(finalBuffer, metadata);
10719
- const absolutePath = path19.resolve(process.cwd(), outputPath);
10720
- await fs20.ensureDir(path19.dirname(absolutePath));
11052
+ const absolutePath = path20.resolve(process.cwd(), outputPath);
11053
+ await fs21.ensureDir(path20.dirname(absolutePath));
10721
11054
  await RevertManager.recordFileChange(absolutePath);
10722
- await fs20.writeFile(absolutePath, finalBuffer);
11055
+ await fs21.writeFile(absolutePath, finalBuffer);
10723
11056
  await recordImageGeneration(settings);
10724
- const ext = path19.extname(outputPath).toLowerCase();
11057
+ const ext = path20.extname(outputPath).toLowerCase();
10725
11058
  const mimeMap = {
10726
11059
  ".jpg": "image/jpeg",
10727
11060
  ".jpeg": "image/jpeg",
@@ -10836,13 +11169,13 @@ var init_addMemScore = __esm({
10836
11169
  });
10837
11170
 
10838
11171
  // src/utils/parsers.js
10839
- import fs21 from "fs-extra";
10840
- import path20 from "path";
11172
+ import fs22 from "fs-extra";
11173
+ import path21 from "path";
10841
11174
  import https from "https";
10842
11175
  async function downloadWasm(wasmFile, targetUrl = null) {
10843
11176
  const url = targetUrl || `https://unpkg.com/tree-sitter-wasms@0.1.13/out/${wasmFile}`;
10844
- const localPath = path20.join(PARSER_DIR, wasmFile);
10845
- await fs21.ensureDir(PARSER_DIR);
11177
+ const localPath = path21.join(PARSER_DIR, wasmFile);
11178
+ await fs22.ensureDir(PARSER_DIR);
10846
11179
  return new Promise((resolve, reject) => {
10847
11180
  const options = {
10848
11181
  headers: {
@@ -10863,27 +11196,27 @@ async function downloadWasm(wasmFile, targetUrl = null) {
10863
11196
  reject(new Error(`Failed to download ${wasmFile}: HTTP ${response.statusCode}`));
10864
11197
  return;
10865
11198
  }
10866
- const file = fs21.createWriteStream(localPath);
11199
+ const file = fs22.createWriteStream(localPath);
10867
11200
  response.pipe(file);
10868
11201
  file.on("finish", () => {
10869
11202
  file.close();
10870
11203
  resolve();
10871
11204
  });
10872
11205
  }).on("error", (err) => {
10873
- if (fs21.existsSync(localPath)) fs21.unlink(localPath, () => {
11206
+ if (fs22.existsSync(localPath)) fs22.unlink(localPath, () => {
10874
11207
  });
10875
11208
  reject(err);
10876
11209
  });
10877
11210
  });
10878
11211
  }
10879
11212
  function isParserInstalled(wasmFile) {
10880
- const localPath = path20.join(PARSER_DIR, wasmFile);
10881
- return fs21.existsSync(localPath);
11213
+ const localPath = path21.join(PARSER_DIR, wasmFile);
11214
+ return fs22.existsSync(localPath);
10882
11215
  }
10883
11216
  async function deleteParser(wasmFile) {
10884
- const localPath = path20.join(PARSER_DIR, wasmFile);
10885
- if (fs21.existsSync(localPath)) {
10886
- await fs21.unlink(localPath);
11217
+ const localPath = path21.join(PARSER_DIR, wasmFile);
11218
+ if (fs22.existsSync(localPath)) {
11219
+ await fs22.unlink(localPath);
10887
11220
  }
10888
11221
  }
10889
11222
  var EXTENSION_TO_WASM;
@@ -10905,8 +11238,8 @@ var init_parsers = __esm({
10905
11238
  });
10906
11239
 
10907
11240
  // src/tools/file_map.js
10908
- import fs22 from "fs-extra";
10909
- import path21 from "path";
11241
+ import fs23 from "fs-extra";
11242
+ import path22 from "path";
10910
11243
  import { createRequire as createRequire2 } from "module";
10911
11244
  function sanitize(text, limit = 50) {
10912
11245
  if (!text) return "";
@@ -11098,17 +11431,17 @@ var init_file_map = __esm({
11098
11431
  if (!filePath) {
11099
11432
  return 'ERROR: No file path provided. Use [tool:functions.FileMap(path="...")]';
11100
11433
  }
11101
- const absolutePath = path21.isAbsolute(filePath) ? filePath : path21.resolve(process.cwd(), filePath);
11102
- if (!fs22.existsSync(absolutePath)) {
11434
+ const absolutePath = path22.isAbsolute(filePath) ? filePath : path22.resolve(process.cwd(), filePath);
11435
+ if (!fs23.existsSync(absolutePath)) {
11103
11436
  return `ERROR: File not found: ${filePath}`;
11104
11437
  }
11105
- const ext = path21.extname(absolutePath).slice(1).toLowerCase();
11438
+ const ext = path22.extname(absolutePath).slice(1).toLowerCase();
11106
11439
  const wasmFile = EXTENSION_TO_WASM[ext];
11107
11440
  if (!wasmFile) {
11108
11441
  return `ERROR: Unsupported file extension: .${ext}`;
11109
11442
  }
11110
- const wasmPath = path21.resolve(PARSER_DIR, wasmFile);
11111
- if (!fs22.existsSync(wasmPath)) {
11443
+ const wasmPath = path22.resolve(PARSER_DIR, wasmFile);
11444
+ if (!fs23.existsSync(wasmPath)) {
11112
11445
  return `ERROR: Parser for .${ext} not found. Please download it in Settings > Other.`;
11113
11446
  }
11114
11447
  try {
@@ -11116,9 +11449,9 @@ var init_file_map = __esm({
11116
11449
  if (!isParserInitialized) {
11117
11450
  let tsWasmPath;
11118
11451
  try {
11119
- tsWasmPath = path21.join(path21.dirname(require3.resolve("web-tree-sitter")), "tree-sitter.wasm");
11452
+ tsWasmPath = path22.join(path22.dirname(require3.resolve("web-tree-sitter")), "tree-sitter.wasm");
11120
11453
  } catch (e) {
11121
- tsWasmPath = path21.join(process.cwd(), "node_modules", "web-tree-sitter", "tree-sitter.wasm");
11454
+ tsWasmPath = path22.join(process.cwd(), "node_modules", "web-tree-sitter", "tree-sitter.wasm");
11122
11455
  }
11123
11456
  await Parser.init({
11124
11457
  locateFile: (p) => {
@@ -11133,7 +11466,7 @@ var init_file_map = __esm({
11133
11466
  const parser = new Parser();
11134
11467
  const Lang = await TreeSitter.Language.load(wasmPath);
11135
11468
  parser.setLanguage(Lang);
11136
- const sourceCode = await fs22.readFile(absolutePath, "utf8");
11469
+ const sourceCode = await fs23.readFile(absolutePath, "utf8");
11137
11470
  const lines = sourceCode.split("\n").length;
11138
11471
  let maxDepth = 12;
11139
11472
  if (lines > 1e4) maxDepth = 2;
@@ -11156,8 +11489,8 @@ Stack: ${err.stack}` : "";
11156
11489
  });
11157
11490
 
11158
11491
  // src/tools/todo.js
11159
- import fs23 from "fs";
11160
- import path22 from "path";
11492
+ import fs24 from "fs";
11493
+ import path23 from "path";
11161
11494
  var todo;
11162
11495
  var init_todo = __esm({
11163
11496
  "src/tools/todo.js"() {
@@ -11168,8 +11501,8 @@ var init_todo = __esm({
11168
11501
  const { method, tasks, markDone } = parseArgs(args);
11169
11502
  const chatId = context.chatId || "default";
11170
11503
  if (!method) return 'ERROR: Missing "method" argument for todo tool (create/append/get).';
11171
- const todoDir = path22.join(DATA_DIR, "plan", chatId);
11172
- const todoFile = path22.join(todoDir, "todo.md");
11504
+ const todoDir = path23.join(DATA_DIR, "plan", chatId);
11505
+ const todoFile = path23.join(todoDir, "todo.md");
11173
11506
  const parseMessyArray = (input) => {
11174
11507
  if (!input || Array.isArray(input)) return input;
11175
11508
  const trimmed = String(input).trim();
@@ -11229,8 +11562,8 @@ var init_todo = __esm({
11229
11562
  };
11230
11563
  };
11231
11564
  try {
11232
- if (!fs23.existsSync(todoDir)) {
11233
- fs23.mkdirSync(todoDir, { recursive: true });
11565
+ if (!fs24.existsSync(todoDir)) {
11566
+ fs24.mkdirSync(todoDir, { recursive: true });
11234
11567
  }
11235
11568
  if (method === "create") {
11236
11569
  if (!tasks) return 'ERROR: Missing "tasks" for create method.';
@@ -11242,7 +11575,7 @@ var init_todo = __esm({
11242
11575
  markedCount = result.markedCount;
11243
11576
  }
11244
11577
  await RevertManager.recordFileChange(todoFile);
11245
- fs23.writeFileSync(todoFile, content, "utf8");
11578
+ fs24.writeFileSync(todoFile, content, "utf8");
11246
11579
  const total = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.startsWith("- [ ]") || l.startsWith("- [x]") || l.startsWith("- [X]")).length;
11247
11580
  if (markedCount > 0) {
11248
11581
  const completed = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.startsWith("- [x]") || l.startsWith("- [X]")).length;
@@ -11256,8 +11589,8 @@ ${content}`;
11256
11589
  if (!tasks) return 'ERROR: Missing "tasks" for append method.';
11257
11590
  const appendContent = getTasksString(tasks);
11258
11591
  await RevertManager.recordFileChange(todoFile);
11259
- fs23.appendFileSync(todoFile, appendContent, "utf8");
11260
- const fullContent = fs23.readFileSync(todoFile, "utf8");
11592
+ fs24.appendFileSync(todoFile, appendContent, "utf8");
11593
+ const fullContent = fs24.readFileSync(todoFile, "utf8");
11261
11594
  const lines = fullContent.split(/\r?\n/).map((l) => l.trim());
11262
11595
  const total = lines.filter((l) => l.startsWith("- [ ]") || l.startsWith("- [x]") || l.startsWith("- [X]")).length;
11263
11596
  const completed = lines.filter((l) => l.startsWith("- [x]") || l.startsWith("- [X]")).length;
@@ -11266,10 +11599,10 @@ ${content}`;
11266
11599
  ${fullContent}`;
11267
11600
  }
11268
11601
  if (method === "get") {
11269
- if (!fs23.existsSync(todoFile)) {
11602
+ if (!fs24.existsSync(todoFile)) {
11270
11603
  return "TODO GET: No task list found for this session.";
11271
11604
  }
11272
- let content = fs23.readFileSync(todoFile, "utf8");
11605
+ let content = fs24.readFileSync(todoFile, "utf8");
11273
11606
  let markedCount = 0;
11274
11607
  if (markDone) {
11275
11608
  const result = applyMarkDone(content, markDone);
@@ -11277,7 +11610,7 @@ ${fullContent}`;
11277
11610
  content = result.content;
11278
11611
  markedCount = result.markedCount;
11279
11612
  await RevertManager.recordFileChange(todoFile);
11280
- fs23.writeFileSync(todoFile, content, "utf8");
11613
+ fs24.writeFileSync(todoFile, content, "utf8");
11281
11614
  }
11282
11615
  }
11283
11616
  const totalLines = content.split(/\r?\n/).map((l) => l.trim());
@@ -11673,20 +12006,20 @@ var init_await = __esm({
11673
12006
  });
11674
12007
 
11675
12008
  // src/utils/advanceRevert.js
11676
- import fs24 from "fs-extra";
11677
- import path23 from "path";
12009
+ import fs25 from "fs-extra";
12010
+ import path24 from "path";
11678
12011
  async function scanWorkspace(dir, baseDir = dir) {
11679
12012
  const manifest = {};
11680
- const entries = await fs24.readdir(dir, { withFileTypes: true }).catch(() => []);
12013
+ const entries = await fs25.readdir(dir, { withFileTypes: true }).catch(() => []);
11681
12014
  for (const entry of entries) {
11682
12015
  if (JUNK_DIRECTORIES.includes(entry.name)) continue;
11683
- const fullPath = path23.join(dir, entry.name);
11684
- const relPath = path23.relative(baseDir, fullPath).replace(/\\/g, "/");
12016
+ const fullPath = path24.join(dir, entry.name);
12017
+ const relPath = path24.relative(baseDir, fullPath).replace(/\\/g, "/");
11685
12018
  if (entry.isDirectory()) {
11686
12019
  const sub = await scanWorkspace(fullPath, baseDir);
11687
12020
  Object.assign(manifest, sub);
11688
12021
  } else {
11689
- const stats = await fs24.stat(fullPath).catch(() => null);
12022
+ const stats = await fs25.stat(fullPath).catch(() => null);
11690
12023
  if (stats) {
11691
12024
  manifest[relPath] = {
11692
12025
  size: stats.size,
@@ -11698,34 +12031,34 @@ async function scanWorkspace(dir, baseDir = dir) {
11698
12031
  return manifest;
11699
12032
  }
11700
12033
  async function copyWorkspaceFiles(destDir, manifest) {
11701
- await fs24.ensureDir(destDir);
12034
+ await fs25.ensureDir(destDir);
11702
12035
  for (const relPath of Object.keys(manifest)) {
11703
- const srcPath = path23.join(process.cwd(), relPath);
11704
- const destPath = path23.join(destDir, relPath);
11705
- await fs24.ensureDir(path23.dirname(destPath));
11706
- await fs24.copyFile(srcPath, destPath).catch(() => {
12036
+ const srcPath = path24.join(process.cwd(), relPath);
12037
+ const destPath = path24.join(destDir, relPath);
12038
+ await fs25.ensureDir(path24.dirname(destPath));
12039
+ await fs25.copyFile(srcPath, destPath).catch(() => {
11707
12040
  });
11708
12041
  }
11709
12042
  }
11710
12043
  async function restoreSnapshotDir(srcDir, destDir, stats = null, baseDir = null) {
11711
- if (!await fs24.pathExists(srcDir)) return;
12044
+ if (!await fs25.pathExists(srcDir)) return;
11712
12045
  if (!baseDir) baseDir = srcDir;
11713
- const entries = await fs24.readdir(srcDir, { withFileTypes: true }).catch(() => []);
12046
+ const entries = await fs25.readdir(srcDir, { withFileTypes: true }).catch(() => []);
11714
12047
  for (const entry of entries) {
11715
- const srcPath = path23.join(srcDir, entry.name);
11716
- const destPath = path23.join(destDir, entry.name);
12048
+ const srcPath = path24.join(srcDir, entry.name);
12049
+ const destPath = path24.join(destDir, entry.name);
11717
12050
  if (entry.isDirectory()) {
11718
12051
  await restoreSnapshotDir(srcPath, destPath, stats, baseDir);
11719
12052
  } else {
11720
- const relPath = path23.relative(baseDir, srcPath).replace(/\\/g, "/");
11721
- const existed = await fs24.pathExists(destPath);
12053
+ const relPath = path24.relative(baseDir, srcPath).replace(/\\/g, "/");
12054
+ const existed = await fs25.pathExists(destPath);
11722
12055
  if (existed) {
11723
- await fs24.chmod(destPath, 438).catch(() => {
12056
+ await fs25.chmod(destPath, 438).catch(() => {
11724
12057
  });
11725
12058
  }
11726
- await fs24.ensureDir(path23.dirname(destPath));
11727
- const ok = await fs24.copyFile(srcPath, destPath).then(() => true).catch(() => false);
11728
- await fs24.chmod(destPath, 438).catch(() => {
12059
+ await fs25.ensureDir(path24.dirname(destPath));
12060
+ const ok = await fs25.copyFile(srcPath, destPath).then(() => true).catch(() => false);
12061
+ await fs25.chmod(destPath, 438).catch(() => {
11729
12062
  });
11730
12063
  if (stats) {
11731
12064
  if (!ok) {
@@ -11763,12 +12096,12 @@ var init_advanceRevert = __esm({
11763
12096
  AdvanceRevertManager = {
11764
12097
  async takeInitialSnapshot(chatId) {
11765
12098
  try {
11766
- const snapshotsDir = path23.join(DATA_DIR, "snapshots", chatId);
11767
- await fs24.remove(snapshotsDir).catch(() => {
12099
+ const snapshotsDir = path24.join(DATA_DIR, "snapshots", chatId);
12100
+ await fs25.remove(snapshotsDir).catch(() => {
11768
12101
  });
11769
- await fs24.ensureDir(snapshotsDir);
12102
+ await fs25.ensureDir(snapshotsDir);
11770
12103
  const manifest = await scanWorkspace(process.cwd());
11771
- await copyWorkspaceFiles(path23.join(snapshotsDir, "initial"), manifest);
12104
+ await copyWorkspaceFiles(path24.join(snapshotsDir, "initial"), manifest);
11772
12105
  const ledger = readEncryptedJson(LEDGER_ADVANCE_FILE, {});
11773
12106
  ledger[chatId] = {
11774
12107
  initialManifest: manifest,
@@ -11817,7 +12150,7 @@ var init_advanceRevert = __esm({
11817
12150
  for (const file of changedFiles) {
11818
12151
  deltaManifest[file] = currentManifest[file];
11819
12152
  }
11820
- const turnDir = path23.join(DATA_DIR, "snapshots", chatId, `turn_${turnNumber}`);
12153
+ const turnDir = path24.join(DATA_DIR, "snapshots", chatId, `turn_${turnNumber}`);
11821
12154
  await copyWorkspaceFiles(turnDir, deltaManifest);
11822
12155
  }
11823
12156
  session.checkpoints.push({
@@ -11851,28 +12184,28 @@ var init_advanceRevert = __esm({
11851
12184
  const checkpoints = session.checkpoints || [];
11852
12185
  const targetIdx = checkpoints.findIndex((c) => c.id === checkpointId);
11853
12186
  if (targetIdx === -1) throw new Error(`Checkpoint [${checkpointId}] not found.`);
11854
- const snapshotsDir = path23.join(DATA_DIR, "snapshots", chatId);
12187
+ const snapshotsDir = path24.join(DATA_DIR, "snapshots", chatId);
11855
12188
  const stats = { restored: 0, replaced: 0, failed: [] };
11856
12189
  const currentFiles = await scanWorkspace(process.cwd());
11857
12190
  for (const relPath of Object.keys(currentFiles)) {
11858
- const fullPath = path23.join(process.cwd(), relPath);
11859
- await fs24.chmod(fullPath, 438).catch(() => {
12191
+ const fullPath = path24.join(process.cwd(), relPath);
12192
+ await fs25.chmod(fullPath, 438).catch(() => {
11860
12193
  });
11861
- await fs24.remove(fullPath).catch(() => {
12194
+ await fs25.remove(fullPath).catch(() => {
11862
12195
  });
11863
12196
  }
11864
- const initialDir = path23.join(snapshotsDir, "initial");
12197
+ const initialDir = path24.join(snapshotsDir, "initial");
11865
12198
  await restoreSnapshotDir(initialDir, process.cwd(), stats, initialDir);
11866
12199
  for (let i = 1; i <= targetIdx; i++) {
11867
12200
  const cp = checkpoints[i];
11868
- const turnDir = path23.join(snapshotsDir, cp.id);
12201
+ const turnDir = path24.join(snapshotsDir, cp.id);
11869
12202
  await restoreSnapshotDir(turnDir, process.cwd(), stats, turnDir);
11870
12203
  if (cp.deletedFiles && cp.deletedFiles.length > 0) {
11871
12204
  for (const delFile of cp.deletedFiles) {
11872
- const fullPath = path23.join(process.cwd(), delFile);
11873
- await fs24.chmod(fullPath, 438).catch(() => {
12205
+ const fullPath = path24.join(process.cwd(), delFile);
12206
+ await fs25.chmod(fullPath, 438).catch(() => {
11874
12207
  });
11875
- await fs24.remove(fullPath).catch(() => {
12208
+ await fs25.remove(fullPath).catch(() => {
11876
12209
  });
11877
12210
  }
11878
12211
  }
@@ -11904,8 +12237,8 @@ var init_advanceRevert = __esm({
11904
12237
  },
11905
12238
  async cleanup(chatId) {
11906
12239
  try {
11907
- const snapshotsDir = path23.join(DATA_DIR, "snapshots", chatId);
11908
- await fs24.remove(snapshotsDir).catch(() => {
12240
+ const snapshotsDir = path24.join(DATA_DIR, "snapshots", chatId);
12241
+ await fs25.remove(snapshotsDir).catch(() => {
11909
12242
  });
11910
12243
  const ledger = readEncryptedJson(LEDGER_ADVANCE_FILE, {});
11911
12244
  if (ledger[chatId]) {
@@ -12260,8 +12593,8 @@ __export(ai_exports, {
12260
12593
  });
12261
12594
  import dotenv from "dotenv";
12262
12595
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
12263
- import path24, { normalize } from "path";
12264
- import fs25 from "fs";
12596
+ import path25, { normalize } from "path";
12597
+ import fs26 from "fs";
12265
12598
  var RE_STUTTER_CODE_BLOCK_CLOSED, RE_STUTTER_CODE_BLOCK_OPEN, RE_STUTTER_INLINE_CODE, RE_STUTTER_TABLE_ROW, RE_STUTTER_WORD_BOUNDARY, RE_STUTTER_NON_ALNUM, RE_TOOL_CALL_FUNC, RE_TOOL_PARTIAL_ARGS_FALLBACK, RE_STRIP_QUOTES, RE_BACKSLASH_SLASH, client, globalSettings, colorMainWords, withRetry, TERMINATION_SIGNAL, getCleanGroupedLength, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getMistralStream, getNVIDIAStream, wrapNvidiaStreamWithQueueDepth, getOpenRouterStream, signalTermination, isTerminationSignaled, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, translateKimiToolCalls, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, compressHistory, deleteChatSummary, getAIStream, runSubagent;
12266
12599
  var init_ai = __esm({
12267
12600
  async "src/utils/ai.js"() {
@@ -13240,7 +13573,7 @@ var init_ai = __esm({
13240
13573
  return pArgs.id || pArgs.taskId;
13241
13574
  }
13242
13575
  const filePath = pArgs.path || pArgs.targetFile || pArgs.TargetFile || pArgs.directory;
13243
- return filePath ? path24.basename(filePath.replace(/["']/g, "").replace(/\\/g, "/")) : null;
13576
+ return filePath ? path25.basename(filePath.replace(/["']/g, "").replace(/\\/g, "/")) : null;
13244
13577
  } catch (e) {
13245
13578
  return null;
13246
13579
  }
@@ -13505,9 +13838,9 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
13505
13838
  }
13506
13839
  })() : String(err);
13507
13840
  await new Promise((resolve) => setTimeout(resolve, 1e3));
13508
- const janitorErrDir = path24.join(LOGS_DIR, "janitor");
13509
- if (!fs25.existsSync(janitorErrDir)) fs25.mkdirSync(janitorErrDir, { recursive: true });
13510
- fs25.appendFileSync(path24.join(janitorErrDir, "error.log"), `ERROR [Attempt ${attempts}/${MAX_JANITOR_RETRIES + 1}] [${date}]: ${errLog}
13841
+ const janitorErrDir = path25.join(LOGS_DIR, "janitor");
13842
+ if (!fs26.existsSync(janitorErrDir)) fs26.mkdirSync(janitorErrDir, { recursive: true });
13843
+ fs26.appendFileSync(path25.join(janitorErrDir, "error.log"), `ERROR [Attempt ${attempts}/${MAX_JANITOR_RETRIES + 1}] [${date}]: ${errLog}
13511
13844
 
13512
13845
  `);
13513
13846
  if (attempts > MAX_JANITOR_RETRIES) break;
@@ -13516,8 +13849,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
13516
13849
  }
13517
13850
  }
13518
13851
  if (attempts) {
13519
- const janitorErrDir = path24.join(LOGS_DIR, "janitor");
13520
- fs25.appendFileSync(path24.join(janitorErrDir, "error.log"), `-----------------------------------------------------------------------------
13852
+ const janitorErrDir = path25.join(LOGS_DIR, "janitor");
13853
+ fs26.appendFileSync(path25.join(janitorErrDir, "error.log"), `-----------------------------------------------------------------------------
13521
13854
 
13522
13855
  `);
13523
13856
  }
@@ -14050,10 +14383,10 @@ ${newMemoryListStr}
14050
14383
  }
14051
14384
  })() : String(err);
14052
14385
  ;
14053
- const janitorLogDir = path24.join(LOGS_DIR, "janitor");
14054
- if (!fs25.existsSync(janitorLogDir)) fs25.mkdirSync(janitorLogDir, { recursive: true });
14055
- fs25.appendFileSync(
14056
- path24.join(janitorLogDir, "error.log"),
14386
+ const janitorLogDir = path25.join(LOGS_DIR, "janitor");
14387
+ if (!fs26.existsSync(janitorLogDir)) fs26.mkdirSync(janitorLogDir, { recursive: true });
14388
+ fs26.appendFileSync(
14389
+ path25.join(janitorLogDir, "error.log"),
14057
14390
  `[${(/* @__PURE__ */ new Date()).toLocaleString()}] Past memory batch consolidation error: ${errLog}
14058
14391
  `
14059
14392
  );
@@ -14061,7 +14394,7 @@ ${newMemoryListStr}
14061
14394
  };
14062
14395
  compressHistory = async (settings, history, isAuto = false) => {
14063
14396
  const { chatId, aiProvider = "Google" } = settings;
14064
- const summariesFile = path24.join(SECRET_DIR, "chat-summaries.json");
14397
+ const summariesFile = path25.join(SECRET_DIR, "chat-summaries.json");
14065
14398
  const flattenContext = (hist) => {
14066
14399
  return hist.filter(
14067
14400
  (m) => (m.role === "user" || m.role === "agent" || m.role === "system") && m.role !== "think" && !m.isVisualFeedback && !m.isMeta && !String(m.id).startsWith("welcome")
@@ -14136,8 +14469,8 @@ Provide a consolidated summary of the entire session.`;
14136
14469
  };
14137
14470
  deleteChatSummary = (chatId) => {
14138
14471
  try {
14139
- const summariesFile = path24.join(SECRET_DIR, "chat-summaries.json");
14140
- if (fs25.existsSync(summariesFile)) {
14472
+ const summariesFile = path25.join(SECRET_DIR, "chat-summaries.json");
14473
+ if (fs26.existsSync(summariesFile)) {
14141
14474
  const summaries = readEncryptedJson(summariesFile, {});
14142
14475
  if (summaries[chatId]) {
14143
14476
  delete summaries[chatId];
@@ -14153,7 +14486,7 @@ Provide a consolidated summary of the entire session.`;
14153
14486
  if (!client && aiProvider === "Google") throw new Error("AI not initialized");
14154
14487
  const isMemoryEnabled = systemSettings?.memory !== false;
14155
14488
  const originalText = history[history.length - 1].text;
14156
- const summariesFile = path24.join(SECRET_DIR, "chat-summaries.json");
14489
+ const summariesFile = path25.join(SECRET_DIR, "chat-summaries.json");
14157
14490
  let wasCompressedInStream = false;
14158
14491
  const isFirstPrompt = history.filter((m) => m.role === "user").length === 1;
14159
14492
  const hasTitleSignal = originalText.includes("[TITLE-UPDATE]");
@@ -14399,7 +14732,7 @@ Provide a consolidated summary of the entire session.`;
14399
14732
  ];
14400
14733
  const safeReaddirWithTypes = (dir) => {
14401
14734
  try {
14402
- return fs25.readdirSync(dir, { withFileTypes: true });
14735
+ return fs26.readdirSync(dir, { withFileTypes: true });
14403
14736
  } catch (e) {
14404
14737
  return [];
14405
14738
  }
@@ -14412,16 +14745,16 @@ Provide a consolidated summary of the entire session.`;
14412
14745
  if (COLLAPSED_DIRS_GLOBAL.includes(entry.name)) continue;
14413
14746
  if (entry.isDirectory()) {
14414
14747
  currentCount.value++;
14415
- countFolders(path24.join(dir, entry.name), currentCount, depth + 1);
14748
+ countFolders(path25.join(dir, entry.name), currentCount, depth + 1);
14416
14749
  }
14417
14750
  }
14418
14751
  return currentCount.value;
14419
14752
  };
14420
14753
  const getDirTree = (dir, maxDepth, prefix = "", depth = 1) => {
14421
14754
  const entries = safeReaddirWithTypes(dir);
14422
- const sep = path24.sep;
14755
+ const sep = path25.sep;
14423
14756
  if (entries.length > 100) {
14424
- return `${prefix}\u2514\u2500\u2500 ${path24.basename(dir)}${sep} ...100+ files...
14757
+ return `${prefix}\u2514\u2500\u2500 ${path25.basename(dir)}${sep} ...100+ files...
14425
14758
  `;
14426
14759
  }
14427
14760
  let result = "";
@@ -14439,7 +14772,7 @@ Provide a consolidated summary of the entire session.`;
14439
14772
  ];
14440
14773
  finalItems.forEach((item, index) => {
14441
14774
  const isLast = index === finalItems.length - 1;
14442
- const filePath = path24.join(dir, item.name);
14775
+ const filePath = path25.join(dir, item.name);
14443
14776
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
14444
14777
  const childPrefix = prefix + (isLast ? " " : "\u2502 ");
14445
14778
  if (item.isCollapsed) {
@@ -14513,19 +14846,20 @@ ${currentSummary}
14513
14846
  }
14514
14847
  const activeSummaryBlock = currentSummary && !hasExistingTurnsAfterCompression ? `
14515
14848
  [SYSTEM METADATA]
14516
- **CONTEXT SUMMARY OF PREVIOUS TURNS (PRIORITY: DYNAMIC)**
14849
+ **CONTEXT SUMMARY OF PREVIOUS TURNS**
14517
14850
  ${currentSummary}
14518
14851
  ` : "";
14519
- let dirStructure = process.cwd() + "\n" + getDirTree(process.cwd(), dynamicMaxDepth);
14852
+ let dirStructure = "CWD: " + process.cwd() + `${isPlayground ? " [PLAYGROUND MODE]" : ""}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${lastCwd})` : ""}
14853
+ ` + getDirTree(process.cwd(), dynamicMaxDepth);
14520
14854
  const ideCtx = await getIDEContext();
14521
14855
  let ideBlock = "";
14522
14856
  if (isBridgeConnected()) {
14523
14857
  ideBlock = "[IDE CONTEXT]\n";
14524
14858
  if (ideCtx.file_focused !== "none") {
14525
- const relFocused = path24.relative(process.cwd(), ideCtx.file_focused);
14859
+ const relFocused = path25.relative(process.cwd(), ideCtx.file_focused);
14526
14860
  const relOpened = (ideCtx.opened_editors || []).map((p) => {
14527
- const rel = path24.relative(process.cwd(), p);
14528
- return rel.startsWith("..") ? `[External] ${path24.basename(p)}` : rel;
14861
+ const rel = path25.relative(process.cwd(), p);
14862
+ return rel.startsWith("..") ? `[External] ${path25.basename(p)}` : rel;
14529
14863
  });
14530
14864
  ideBlock += `Focused File: ${relFocused}
14531
14865
  Cursor Line: ${ideCtx.cursor_line}
@@ -14567,7 +14901,7 @@ Cursor Line: ${ideCtx.cursor_line}
14567
14901
  }
14568
14902
  const getSumForLimit = (limit, activeFiles2) => {
14569
14903
  return activeFiles2.reduce((sum, f) => {
14570
- const isFocused = ideCtx.file_focused && (f.path === ideCtx.file_focused || path24.resolve(process.cwd(), f.path) === path24.resolve(ideCtx.file_focused));
14904
+ const isFocused = ideCtx.file_focused && (f.path === ideCtx.file_focused || path25.resolve(process.cwd(), f.path) === path25.resolve(ideCtx.file_focused));
14571
14905
  const fileLimit = isFocused ? Math.ceil(limit * 1.2) : limit;
14572
14906
  return sum + Math.min(f.edits.length, fileLimit);
14573
14907
  }, 0);
@@ -14601,7 +14935,7 @@ Cursor Line: ${ideCtx.cursor_line}
14601
14935
  }
14602
14936
  }
14603
14937
  for (const file of activeFiles) {
14604
- const isFocused = ideCtx.file_focused && (file.path === ideCtx.file_focused || path24.resolve(process.cwd(), file.path) === path24.resolve(ideCtx.file_focused));
14938
+ const isFocused = ideCtx.file_focused && (file.path === ideCtx.file_focused || path25.resolve(process.cwd(), file.path) === path25.resolve(ideCtx.file_focused));
14605
14939
  const fileLimit = isFocused ? Math.ceil(chosenLimit * 1.2) : chosenLimit;
14606
14940
  if (file.edits.length > fileLimit) {
14607
14941
  file.edits = file.edits.slice(-fileLimit);
@@ -14688,9 +15022,9 @@ ${ideCtx.warnings}
14688
15022
  endLine = matchRange[2] ? parseInt(matchRange[2], 10) : startLine;
14689
15023
  filePath = tagClean.slice(0, matchRange.index);
14690
15024
  }
14691
- const absPath = path24.resolve(process.cwd(), filePath);
14692
- if (fs25.existsSync(absPath)) {
14693
- const stats = fs25.statSync(absPath);
15025
+ const absPath = path25.resolve(process.cwd(), filePath);
15026
+ if (fs26.existsSync(absPath)) {
15027
+ const stats = fs26.statSync(absPath);
14694
15028
  if (stats.isFile()) {
14695
15029
  const pathLower = filePath.toLowerCase();
14696
15030
  const isPdf = pathLower.endsWith(".pdf");
@@ -14699,7 +15033,7 @@ ${ideCtx.warnings}
14699
15033
  const isMultimodalFile = isImage || isPdf || isOfficeFile;
14700
15034
  const isSupported = aiProvider === "Google" || isModelMultimodal(modelName);
14701
15035
  if (isMultimodalFile && !isSupported) {
14702
- const label = `\u2718 Unsupported Modality: ${path24.basename(filePath)}`;
15036
+ const label = `\u2718 Unsupported Modality: ${path25.basename(filePath)}`;
14703
15037
  let terminalWidth = 115;
14704
15038
  if (process.stdout.isTTY) {
14705
15039
  terminalWidth = process.stdout.columns - 5 || 120;
@@ -14715,11 +15049,11 @@ ${ideCtx.warnings}
14715
15049
  if (startLine === null && !isMultimodalFile) {
14716
15050
  let lineCount = 0;
14717
15051
  try {
14718
- lineCount = fs25.readFileSync(absPath, "utf8").split(/\r\n|\r|\n/).length;
15052
+ lineCount = fs26.readFileSync(absPath, "utf8").split(/\r\n|\r|\n/).length;
14719
15053
  } catch (e) {
14720
15054
  }
14721
15055
  if (lineCount > 550) {
14722
- const label = `\u21B7 Skipped (Too Large): ${path24.basename(filePath)}`;
15056
+ const label = `\u21B7 Skipped (Too Large): ${path25.basename(filePath)}`;
14723
15057
  let terminalWidth = 115;
14724
15058
  if (process.stdout.isTTY) {
14725
15059
  terminalWidth = process.stdout.columns - 5 || 120;
@@ -14760,13 +15094,13 @@ ${ideCtx.warnings}
14760
15094
  if (!isError) {
14761
15095
  let label = "";
14762
15096
  if (isImage) {
14763
- label = `\u2714 Processed: ${path24.basename(filePath)}`;
15097
+ label = `\u2714 Processed: ${path25.basename(filePath)}`;
14764
15098
  attachedBinaryPart = binPart;
14765
15099
  } else if (isPdf || isOfficeFile) {
14766
- label = `\u2714 Auto-Analysed: ${path24.basename(filePath)}`;
15100
+ label = `\u2714 Auto-Analysed: ${path25.basename(filePath)}`;
14767
15101
  attachedBinaryPart = binPart;
14768
15102
  } else {
14769
- label = `\u2714 Auto-Read: ${path24.basename(filePath)}`;
15103
+ label = `\u2714 Auto-Read: ${path25.basename(filePath)}`;
14770
15104
  taggedContextBlocks.push(textResult);
14771
15105
  }
14772
15106
  if (label) {
@@ -14793,12 +15127,12 @@ ${ideCtx.warnings}
14793
15127
  }
14794
15128
  const osDetected = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
14795
15129
  const cleanPromptForModel = cleanAgentText.replace(/\\(@\[[^\]]+\])/g, "$1");
14796
- const firstUserMsg = `[SYSTEM METADATA (PRIORITY: DYNAMIC), Chat Context >> Metadata] Time: ${dateTimeStr}
15130
+ const firstUserMsg = `[SYSTEM METADATA, Chat Context > Metadata]
15131
+ Time: ${dateTimeStr}
14797
15132
  OS: ${osDetected}
14798
- CWD: ${process.cwd()}${isPlayground ? " [PLAYGROUND MODE]" : ""}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${lastCwd})` : ""}
14799
15133
  **DIRECTORY STRUCTURE**
14800
15134
  ${dirStructure}${memoryPrompt}${ideBlock}
14801
- ${activeSummaryBlock}${thinkingLevel !== "Fast" && (aiProvider === "Mistral" || thinkingLevel !== "xHigh" && aiProvider === "Google") ? `${aiProvider === "Mistral" || modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS HIGH PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[SYSTEM Priority: HIGH] ONLY use the system tool schema. eg: [tool:functions.ReadFolder(path=".")] [/SYSTEM]
15135
+ ${activeSummaryBlock}${thinkingLevel !== "Fast" && (aiProvider === "Mistral" || thinkingLevel !== "xHigh" && aiProvider === "Google") ? `${aiProvider === "Mistral" || modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS HIGH PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[SYSTEM Priority: HIGH] ONLY use the system prompt tool schema. eg: [tool:functions.ReadFolder(path=".")] [/SYSTEM]
14802
15136
  ${taggedContextStr}[USER PROMPT] ${cleanPromptForModel.trim()} [/USER PROMPT]`.trim();
14803
15137
  const userMsgObj = { role: "user", text: firstUserMsg };
14804
15138
  if (attachedBinaryPart) {
@@ -15441,7 +15775,7 @@ ${ideErr} [/ERROR]`;
15441
15775
  if (keyword !== void 0 && keyword !== null) {
15442
15776
  detail = String(keyword).replace(RE_STRIP_QUOTES, "");
15443
15777
  } else if (filePath) {
15444
- detail = path24.basename(String(filePath).replace(RE_STRIP_QUOTES, "").replace(RE_BACKSLASH_SLASH, "/"));
15778
+ detail = path25.basename(String(filePath).replace(RE_STRIP_QUOTES, "").replace(RE_BACKSLASH_SLASH, "/"));
15445
15779
  } else if (title && (potentialTool === "invoke" || potentialTool === "invoke_sync")) {
15446
15780
  detail = String(title).replace(RE_STRIP_QUOTES, "").substring(0, 30);
15447
15781
  } else if (id && potentialTool === "get_progress") {
@@ -15470,7 +15804,7 @@ ${ideErr} [/ERROR]`;
15470
15804
  if (potentialTool === "invoke" || potentialTool === "invoke_sync" || potentialTool === "get_progress") {
15471
15805
  detail = val.substring(0, 30);
15472
15806
  } else {
15473
- detail = potentialTool === "search_keyword" || potentialTool === "file_map" ? val : path24.basename(val.replace(RE_BACKSLASH_SLASH, "/"));
15807
+ detail = potentialTool === "search_keyword" || potentialTool === "file_map" ? val : path25.basename(val.replace(RE_BACKSLASH_SLASH, "/"));
15474
15808
  }
15475
15809
  }
15476
15810
  }
@@ -15676,9 +16010,9 @@ ${ideErr} [/ERROR]`;
15676
16010
  let totalLines = "...";
15677
16011
  let actualEndLine = eLine;
15678
16012
  try {
15679
- const absPath = path24.resolve(process.cwd(), targetPath2);
15680
- if (fs25.existsSync(absPath)) {
15681
- const content = fs25.readFileSync(absPath, "utf8");
16013
+ const absPath = path25.resolve(process.cwd(), targetPath2);
16014
+ if (fs26.existsSync(absPath)) {
16015
+ const content = fs26.readFileSync(absPath, "utf8");
15682
16016
  const lines = content.split("\n").length;
15683
16017
  totalLines = lines;
15684
16018
  actualEndLine = Math.min(eLine, lines);
@@ -15690,16 +16024,16 @@ ${ideErr} [/ERROR]`;
15690
16024
  const isOfficeFile = pathLower.endsWith(".docx") || pathLower.endsWith(".doc") || pathLower.endsWith(".ppt") || pathLower.endsWith(".pptx") || pathLower.endsWith(".xls") || pathLower.endsWith(".xlsx");
15691
16025
  const isImage = /\.(png|jpg|jpeg|webp|gif|bmp)$/.test(pathLower);
15692
16026
  if (isPdf || isOfficeFile) {
15693
- label = `\u2714 Analyzed: ${path24.basename(targetPath2)}`;
16027
+ label = `\u2714 Analyzed: ${path25.basename(targetPath2)}`;
15694
16028
  } else if (isImage) {
15695
- label = `\u2714 Processed: ${path24.basename(targetPath2)}`;
16029
+ label = `\u2714 Processed: ${path25.basename(targetPath2)}`;
15696
16030
  } else {
15697
- label = `${totalLines !== "..." ? "\u2714" : "\u2718"} Read: ${path24.basename(targetPath2)} \u2192 ${totalLines !== "..." ? `Lines ${sLine} - ${actualEndLine} of ${totalLines}` : "File Not Found"}`;
16031
+ label = `${totalLines !== "..." ? "\u2714" : "\u2718"} Read: ${path25.basename(targetPath2)} \u2192 ${totalLines !== "..." ? `Lines ${sLine} - ${actualEndLine} of ${totalLines}` : "File Not Found"}`;
15698
16032
  }
15699
16033
  } else if (normToolName === "list_files" || normToolName === "read_folder") {
15700
16034
  const action = normToolName === "list_files" ? "List" : "Browsed";
15701
- const path26 = parseArgs(toolCall.args).path;
15702
- label = `\u2714 ${action}: ${path26 === "." ? "./" : path26}`;
16035
+ const path27 = parseArgs(toolCall.args).path;
16036
+ label = `\u2714 ${action}: ${path27 === "." ? "./" : path27}`;
15703
16037
  } else if (normToolName === "write_file" || normToolName === "update_file") {
15704
16038
  const action = normToolName === "write_file" ? "Created" : "Edited";
15705
16039
  label = `\u2714 ${action}: ${parseArgs(toolCall.args).path || "..."}`;
@@ -15710,8 +16044,8 @@ ${ideErr} [/ERROR]`;
15710
16044
  label = `\u2714 Generated: ${parseArgs(toolCall.args).path || "..."}
15711
16045
  `;
15712
16046
  } else if (normToolName === "file_map") {
15713
- const path26 = parseArgs(toolCall.args).path;
15714
- label = `${path26 ? "\u2714" : "\u2718"} Indexed${path26 ? ": " + path26 : " File Not Found"}`;
16047
+ const path27 = parseArgs(toolCall.args).path;
16048
+ label = `${path27 ? "\u2714" : "\u2718"} Indexed${path27 ? ": " + path27 : " File Not Found"}`;
15715
16049
  } else if (normToolName.toLowerCase() === "search_keyword" || normToolName.toLowerCase() === "todo") {
15716
16050
  label = "";
15717
16051
  } else if (normToolName.toLowerCase() === "generate_image") {
@@ -15786,7 +16120,7 @@ ${ideErr} [/ERROR]`;
15786
16120
  const { command } = parseArgs(toolCall.args);
15787
16121
  if (command && settings.systemSettings && settings.systemSettings.allowExternalAccess === false) {
15788
16122
  const riskyPatterns = [/[a-zA-Z]:[\\\/]/i, /^\//, /\.\.[\\\/]/, /\/etc\//, /\/var\//, /\/root\//, /\/bin\//, /\/usr\//];
15789
- const currentDrive = path24.resolve(process.cwd()).substring(0, 3).toLowerCase();
16123
+ const currentDrive = path25.resolve(process.cwd()).substring(0, 3).toLowerCase();
15790
16124
  const splitCommands = (cmdString) => {
15791
16125
  const commands = [];
15792
16126
  let current = "";
@@ -15915,8 +16249,8 @@ ${ideErr} [/ERROR]`;
15915
16249
  const targetPath = parsedArgs.path || parsedArgs.targetPath || null;
15916
16250
  if (targetPath) {
15917
16251
  const isExternalOff = settings.systemSettings && settings.systemSettings.allowExternalAccess === false;
15918
- const absoluteTarget = path24.resolve(targetPath);
15919
- const absoluteCwd = path24.resolve(process.cwd());
16252
+ const absoluteTarget = path25.resolve(targetPath);
16253
+ const absoluteCwd = path25.resolve(process.cwd());
15920
16254
  if (isExternalOff && !absoluteTarget.startsWith(absoluteCwd)) {
15921
16255
  const denyMsg = `Access Denied. You are not allowed to access files outside the current workspace.`;
15922
16256
  if (normToolName === "write_file" || normToolName === "update_file") {
@@ -16105,7 +16439,7 @@ ${ideErr} [/ERROR]`;
16105
16439
  const toolArgs = parseArgs(toolCall.args);
16106
16440
  const { path: filePath } = toolArgs;
16107
16441
  if (filePath) {
16108
- const absPath = path24.resolve(process.cwd(), filePath);
16442
+ const absPath = path25.resolve(process.cwd(), filePath);
16109
16443
  const normalize2 = (p) => p ? p.toLowerCase().replace(/\\/g, "/").replace(/^[a-z]:/, (m) => m.toUpperCase()) : "";
16110
16444
  const normAbsPath = normalize2(absPath);
16111
16445
  let originalContent = "";
@@ -16115,8 +16449,8 @@ ${ideErr} [/ERROR]`;
16115
16449
  if (currentIDE && normFocused === normAbsPath && currentIDE.full_content) {
16116
16450
  originalContent = currentIDE.full_content;
16117
16451
  hasOriginal = true;
16118
- } else if (fs25.existsSync(absPath)) {
16119
- originalContent = fs25.readFileSync(absPath, "utf8");
16452
+ } else if (fs26.existsSync(absPath)) {
16453
+ originalContent = fs26.readFileSync(absPath, "utf8");
16120
16454
  hasOriginal = true;
16121
16455
  }
16122
16456
  originalContentForReporting = originalContent;
@@ -16126,7 +16460,7 @@ ${ideErr} [/ERROR]`;
16126
16460
  if (normToolName === "write_file") {
16127
16461
  modifiedContent = toolArgs.content || toolArgs.newContent || "";
16128
16462
  } else {
16129
- const { patchPairs: patches, error: parseError } = parsePatchPairs(toolArgs);
16463
+ const { patchPairs: patches, allowMultiple: parsedAllowMultiple, error: parseError } = parsePatchPairs(toolArgs);
16130
16464
  if (parseError) {
16131
16465
  const errorMsg = `[TOOL RESULT]: ERROR: ${parseError}`;
16132
16466
  toolResults.push({ role: "user", text: errorMsg });
@@ -16136,16 +16470,17 @@ ${ideErr} [/ERROR]`;
16136
16470
  toolCallPointer++;
16137
16471
  continue;
16138
16472
  }
16473
+ const allowMultiple = toolArgs.allowMultiple !== void 0 ? toolArgs.allowMultiple === true || String(toolArgs.allowMultiple).toLowerCase() === "true" : parsedAllowMultiple;
16139
16474
  requestedPatchCount = patches.length;
16140
- const sim = applyPatches(originalContent, patches);
16475
+ const sim = applyPatches(originalContent, patches, { allowMultiple });
16141
16476
  modifiedContent = sim.content;
16142
16477
  patchResults = sim.results;
16143
16478
  const successes = patchResults.filter((r) => r.success);
16144
16479
  const failures = patchResults.filter((r) => !r.success);
16145
16480
  if (successes.length === 0) {
16146
- const errorMsg = `[TOOL RESULT]: ERROR: Failed to apply patches to [${path24.basename(absPath)}].
16481
+ const errorMsg = `[TOOL RESULT]: ERROR: Failed to apply patches to [${path25.basename(absPath)}].
16147
16482
  ${failures.map((f) => ` \u2022 ${f.error}`).join("\n")}`;
16148
- const errorLabel = `\u2714 Edited: ${path24.basename(absPath)}`.toUpperCase();
16483
+ const errorLabel = `\u2714 Edited: ${path25.basename(absPath)}`.toUpperCase();
16149
16484
  let terminalWidth = 115;
16150
16485
  if (process.stdout.isTTY) {
16151
16486
  terminalWidth = process.stdout.columns - 5 || 120;
@@ -16163,19 +16498,19 @@ ${failures.map((f) => ` \u2022 ${f.error}`).join("\n")}`;
16163
16498
  continue;
16164
16499
  }
16165
16500
  }
16166
- yield { type: "status", content: `Opening Diff in IDE: ${path24.basename(absPath)}` };
16501
+ yield { type: "status", content: `Opening Diff in IDE: ${path25.basename(absPath)}` };
16167
16502
  showDiffInIDE(absPath, originalContent, modifiedContent);
16168
16503
  diffOpened = true;
16169
16504
  await new Promise((r) => setTimeout(r, 50));
16170
16505
  } else if (normToolName === "write_file") {
16171
16506
  const rawContent = toolArgs.content || toolArgs.newContent || "";
16172
16507
  const modifiedContent = rawContent.endsWith("\n") ? rawContent : rawContent + "\n";
16173
- if (!fs25.existsSync(absPath)) {
16508
+ if (!fs26.existsSync(absPath)) {
16174
16509
  isNewFileCreated = true;
16175
- fs25.mkdirSync(path24.dirname(absPath), { recursive: true });
16176
- fs25.writeFileSync(absPath, "", "utf8");
16510
+ fs26.mkdirSync(path25.dirname(absPath), { recursive: true });
16511
+ fs26.writeFileSync(absPath, "", "utf8");
16177
16512
  }
16178
- yield { type: "status", content: `Opening New File Diff in IDE: ${path24.basename(absPath)}` };
16513
+ yield { type: "status", content: `Opening New File Diff in IDE: ${path25.basename(absPath)}` };
16179
16514
  showDiffInIDE(absPath, "", modifiedContent);
16180
16515
  diffOpened = true;
16181
16516
  await new Promise((r) => setTimeout(r, 50));
@@ -16211,11 +16546,11 @@ ${failures.map((f) => ` \u2022 ${f.error}`).join("\n")}`;
16211
16546
  if (normToolName === "write_file" || normToolName === "update_file") {
16212
16547
  const { path: filePath } = parseArgs(toolCall.args);
16213
16548
  if (filePath) {
16214
- const absPath = path24.resolve(process.cwd(), filePath);
16549
+ const absPath = path25.resolve(process.cwd(), filePath);
16215
16550
  closeDiffInIDE(absPath, approval);
16216
- if (approval === "deny" && isNewFileCreated && fs25.existsSync(absPath)) {
16551
+ if (approval === "deny" && isNewFileCreated && fs26.existsSync(absPath)) {
16217
16552
  try {
16218
- fs25.unlinkSync(absPath);
16553
+ fs26.unlinkSync(absPath);
16219
16554
  } catch (e) {
16220
16555
  }
16221
16556
  }
@@ -16227,13 +16562,13 @@ ${failures.map((f) => ` \u2022 ${f.error}`).join("\n")}`;
16227
16562
  }
16228
16563
  if (approval === "allow" && diffOpened && isBridgeConnected()) {
16229
16564
  const { path: filePath } = parseArgs(toolCall.args);
16230
- const absPath = path24.resolve(process.cwd(), filePath);
16565
+ const absPath = path25.resolve(process.cwd(), filePath);
16231
16566
  const finalIDE = await getIDEContext();
16232
16567
  let finalContent = "";
16233
16568
  if (finalIDE && finalIDE.file_focused === absPath && finalIDE.full_content) {
16234
16569
  finalContent = finalIDE.full_content;
16235
- } else if (fs25.existsSync(absPath)) {
16236
- finalContent = fs25.readFileSync(absPath, "utf8");
16570
+ } else if (fs26.existsSync(absPath)) {
16571
+ finalContent = fs26.readFileSync(absPath, "utf8");
16237
16572
  }
16238
16573
  const verifiedLines = finalContent.split(/\r?\n/);
16239
16574
  const verifiedLineCount = verifiedLines.length;
@@ -16371,8 +16706,9 @@ ${snippet2}
16371
16706
  }
16372
16707
  if (lastToolFinishedAt > 0) {
16373
16708
  const timeSinceLastTool = Date.now() - lastToolFinishedAt;
16374
- if (timeSinceLastTool < 1500) {
16375
- await new Promise((resolve) => setTimeout(resolve, 1e3 - timeSinceLastTool));
16709
+ const delay = Math.max(0, 1e3 - timeSinceLastTool);
16710
+ if (delay > 0) {
16711
+ await new Promise((resolve) => setTimeout(resolve, delay));
16376
16712
  }
16377
16713
  }
16378
16714
  let execToolContext = {
@@ -16395,7 +16731,7 @@ ${snippet2}
16395
16731
  try {
16396
16732
  const { path: filePath } = parseArgs(toolCall.args);
16397
16733
  if (filePath) {
16398
- const absPath = path24.resolve(process.cwd(), filePath);
16734
+ const absPath = path25.resolve(process.cwd(), filePath);
16399
16735
  const currentIDE = await getIDEContext();
16400
16736
  if (currentIDE && currentIDE.file_focused === absPath && currentIDE.full_content) {
16401
16737
  execToolContext.forcedContent = currentIDE.full_content;
@@ -16409,7 +16745,7 @@ ${snippet2}
16409
16745
  if ((normToolName === "write_file" || normToolName === "update_file") && result.startsWith("SUCCESS")) {
16410
16746
  const { path: filePath } = parseArgs(toolCall.args);
16411
16747
  if (filePath) {
16412
- const absPath = path24.resolve(process.cwd(), filePath);
16748
+ const absPath = path25.resolve(process.cwd(), filePath);
16413
16749
  openFileInEditor(absPath);
16414
16750
  }
16415
16751
  }
@@ -16426,7 +16762,7 @@ ${snippet2}
16426
16762
  result = result.text;
16427
16763
  }
16428
16764
  if (normToolName === "search_keyword") {
16429
- const { keyword, path: path26 } = parseArgs(toolCall.args);
16765
+ const { keyword, path: path27 } = parseArgs(toolCall.args);
16430
16766
  const _isDir = typeof result === "string" && result.startsWith("[DIR]");
16431
16767
  if (_isDir) result = result.slice(5);
16432
16768
  let matchCount = 0;
@@ -16436,7 +16772,7 @@ ${snippet2}
16436
16772
  matchCount = parseInt(m[1]);
16437
16773
  }
16438
16774
  }
16439
- const _sp = path26 ? path26.replace(/[\/\\]+$/, "") : null;
16775
+ const _sp = path27 ? path27.replace(/[\/\\]+$/, "") : null;
16440
16776
  const displayPath = _sp && _sp !== "." ? `"${_isDir ? `${_sp}/*` : _sp}"` : "./";
16441
16777
  const postLabel = `\u2714 Searched: "${keyword}" in ${displayPath} \u2192 ${matchCount} Match${matchCount === 1 ? "" : "es"}`;
16442
16778
  let terminalWidth = 115;
@@ -16676,9 +17012,9 @@ ${snippet2}
16676
17012
  })() : String(err);
16677
17013
  ;
16678
17014
  const date = (/* @__PURE__ */ new Date()).toLocaleString();
16679
- const agentErrDir = path24.join(LOGS_DIR, "agent");
16680
- if (!fs25.existsSync(agentErrDir)) fs25.mkdirSync(agentErrDir, { recursive: true });
16681
- fs25.appendFileSync(path24.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errLog}
17015
+ const agentErrDir = path25.join(LOGS_DIR, "agent");
17016
+ if (!fs26.existsSync(agentErrDir)) fs26.mkdirSync(agentErrDir, { recursive: true });
17017
+ fs26.appendFileSync(path25.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errLog}
16682
17018
 
16683
17019
  ----------------------------------------------------------------------
16684
17020
 
@@ -16725,7 +17061,7 @@ ${recoveryText}`
16725
17061
  yield { type: "status", content: `Error Occured. Recovering Stream...` };
16726
17062
  } else {
16727
17063
  throw new Error(`Stream collapsed too many times. (Failed to resolve ${MAX_RETRIES} times)
16728
- Error Log can be found in ${path24.join(LOGS_DIR, "agent", "error.log")}`);
17064
+ Error Log can be found in ${path25.join(LOGS_DIR, "agent", "error.log")}`);
16729
17065
  }
16730
17066
  } else {
16731
17067
  if (retryCount <= MAX_RETRIES) {
@@ -16743,7 +17079,7 @@ Error Log can be found in ${path24.join(LOGS_DIR, "agent", "error.log")}`);
16743
17079
  yield { type: "status", content: `Trying to reach ${modelName}` };
16744
17080
  } else {
16745
17081
  throw new Error(`Model ${modelName} cannot be reached. (Failed ${MAX_RETRIES} times)
16746
- Error Log can be found in ${path24.join(LOGS_DIR, "agent", "error.log")}`);
17082
+ Error Log can be found in ${path25.join(LOGS_DIR, "agent", "error.log")}`);
16747
17083
  }
16748
17084
  }
16749
17085
  }
@@ -16862,10 +17198,10 @@ Error Log can be found in ${path24.join(LOGS_DIR, "agent", "error.log")}`);
16862
17198
  }
16863
17199
  })() : String(err);
16864
17200
  const date = (/* @__PURE__ */ new Date()).toLocaleString();
16865
- const agentErrDir = path24.join(LOGS_DIR, "agent");
17201
+ const agentErrDir = path25.join(LOGS_DIR, "agent");
16866
17202
  yield { type: "text", content: `\u274C CRITICAL ERROR: ${errLog}` };
16867
- if (!fs25.existsSync(agentErrDir)) fs25.mkdirSync(agentErrDir, { recursive: true });
16868
- fs25.appendFileSync(path24.join(agentErrDir, "error.log"), `CRITICAL ERROR [${date}]: ${err}
17203
+ if (!fs26.existsSync(agentErrDir)) fs26.mkdirSync(agentErrDir, { recursive: true });
17204
+ fs26.appendFileSync(path25.join(agentErrDir, "error.log"), `CRITICAL ERROR [${date}]: ${err}
16869
17205
 
16870
17206
  ----------------------------------------------------------------------
16871
17207
 
@@ -16899,7 +17235,7 @@ Error Log can be found in ${path24.join(LOGS_DIR, "agent", "error.log")}`);
16899
17235
  "readfile": '- [tool:functions.ReadFile(path="...", startLine=number, endLine=number)]. View files',
16900
17236
  "readfolder": '- [tool:functions.ReadFolder(path="...")]. Detailed DIR stats including File Sizes',
16901
17237
  "filemap": '- [tool:functions.FileMap(path="path/file")]. Shows file structure, functions, class, import/export, variables',
16902
- "patchfile": '- [tool:functions.PatchFile(path="...", replaceContent1="full line/block", newContent1="...", ...MAX 10)]. Surgical Patch. **Multiple patch on same file/path? Use replaceContent2, newContent2 etc >>> multiple spams**. Unsure? ReadFile >> guessing. **MUST VERIFY DIFF**',
17238
+ "patchfile": '- [tool:functions.PatchFile(path="...", allowMultiple="true optional", replaceContent1="...", newContent1="...", ...MAX 10)]. Surgical patch. allowMultiple: Replace all matches (default: false). Multiple patches same file? Use replaceContent2/newContent2... Unsure? ReadFile. MUST VERIFY DIFF',
16903
17239
  "writefile": '- [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. Verify Imports',
16904
17240
  "searchkeyword": '- [tool:functions.SearchKeyword(keyword="...", path="optional, target directory or filename", subString="true optional", regex="false for keyword, optional")]. Project-wide search. path limits scope to a file/dir. Find definitions/logic without full reads. Locate relevant code. Defaults: subString=false, regex=true',
16905
17241
  "websearch": '- [tool:functions.WebSearch(query="...", aiMode="true optional", limit=number)]. Limit 3-10 (aiMode ignores). Usage: unknown info/docs. aiMode: LLM search (default: false)',
@@ -17009,20 +17345,20 @@ ${cleanResponse}
17009
17345
  } else if (normalizedToolName === "web_scrape" || normalizedToolName === "webscrape") {
17010
17346
  label = `\u2714 \x1B[95mScraped\x1B[0m`;
17011
17347
  } else if (normalizedToolName === "view_file" || normalizedToolName === "viewfile" || normalizedToolName === "readfile") {
17012
- const path26 = parseArgs(toolCall.args).path || "";
17013
- label = `\u2714 \x1B[95mRead\x1B[0m: ${path26}`;
17348
+ const path27 = parseArgs(toolCall.args).path || "";
17349
+ label = `\u2714 \x1B[95mRead\x1B[0m: ${path27}`;
17014
17350
  } else if (normalizedToolName === "list_files" || normalizedToolName === "read_folder" || normalizedToolName === "readfolder") {
17015
- const path26 = parseArgs(toolCall.args).path || "";
17016
- label = `\u2714 \x1B[95mBrowsed\x1B[0m: ${path26}`;
17351
+ const path27 = parseArgs(toolCall.args).path || "";
17352
+ label = `\u2714 \x1B[95mBrowsed\x1B[0m: ${path27}`;
17017
17353
  } else if (normalizedToolName === "write_file" || normalizedToolName === "writefile") {
17018
- const path26 = parseArgs(toolCall.args).path || "";
17019
- label = `\u2714 \x1B[95mCreated\x1B[0m: ${path26}`;
17354
+ const path27 = parseArgs(toolCall.args).path || "";
17355
+ label = `\u2714 \x1B[95mCreated\x1B[0m: ${path27}`;
17020
17356
  } else if (normalizedToolName === "update_file" || normalizedToolName === "updatefile" || normalizedToolName === "patchfile" || normalizedToolName === "patch_file" || normalizedToolName === "patchfile" || normalizedToolName === "updatefile") {
17021
- const path26 = parseArgs(toolCall.args).path || "";
17022
- label = `\u2714 \x1B[95mEdited\x1B[0m: ${path26}`;
17357
+ const path27 = parseArgs(toolCall.args).path || "";
17358
+ label = `\u2714 \x1B[95mEdited\x1B[0m: ${path27}`;
17023
17359
  } else if (normalizedToolName === "file_map" || normalizedToolName === "filemap") {
17024
- const path26 = parseArgs(toolCall.args).path || "";
17025
- label = `\u2714 \x1B[95mIndexed\x1B[0m: ${path26}`;
17360
+ const path27 = parseArgs(toolCall.args).path || "";
17361
+ label = `\u2714 \x1B[95mIndexed\x1B[0m: ${path27}`;
17026
17362
  } else if (normalizedToolName === "await") {
17027
17363
  const { time } = parseArgs(toolCall.args);
17028
17364
  let sec = parseFloat(time) || 0;
@@ -18017,7 +18353,7 @@ var init_RevertModal = __esm({
18017
18353
  import puppeteer4 from "puppeteer";
18018
18354
  import { exec } from "child_process";
18019
18355
  import { promisify } from "util";
18020
- import fs26 from "fs";
18356
+ import fs27 from "fs";
18021
18357
  var execAsync, checkPuppeteerReady, installPuppeteerBrowser;
18022
18358
  var init_setup = __esm({
18023
18359
  "src/utils/setup.js"() {
@@ -18026,11 +18362,11 @@ var init_setup = __esm({
18026
18362
  checkPuppeteerReady = () => {
18027
18363
  try {
18028
18364
  const pptrConfig = getPuppeteerConfig();
18029
- if (pptrConfig.executablePath && fs26.existsSync(pptrConfig.executablePath)) {
18365
+ if (pptrConfig.executablePath && fs27.existsSync(pptrConfig.executablePath)) {
18030
18366
  return true;
18031
18367
  }
18032
18368
  const exePath = puppeteer4.executablePath();
18033
- const exists = exePath && fs26.existsSync(exePath);
18369
+ const exists = exePath && fs27.existsSync(exePath);
18034
18370
  if (exists) return true;
18035
18371
  } catch (e) {
18036
18372
  return false;
@@ -18117,8 +18453,8 @@ __export(app_exports, {
18117
18453
  import os5 from "os";
18118
18454
  import React16, { useState as useState15, useEffect as useEffect12, useRef as useRef4, useMemo as useMemo2 } from "react";
18119
18455
  import { Box as Box14, Text as Text16, useInput as useInput9, useStdout as useStdout2, Static } from "ink";
18120
- import fs27 from "fs-extra";
18121
- import path25 from "path";
18456
+ import fs28 from "fs-extra";
18457
+ import path26 from "path";
18122
18458
  import { exec as exec2 } from "child_process";
18123
18459
  import { fileURLToPath as fileURLToPath3 } from "url";
18124
18460
  import TextInput4 from "ink-text-input";
@@ -18445,10 +18781,10 @@ function App({ args = [] }) {
18445
18781
  const kbPath = getKeybindingsPath(ideName);
18446
18782
  if (!kbPath) return;
18447
18783
  try {
18448
- await fs27.ensureDir(path25.dirname(kbPath));
18784
+ await fs28.ensureDir(path26.dirname(kbPath));
18449
18785
  let bindings = [];
18450
- if (fs27.existsSync(kbPath)) {
18451
- const content = fs27.readFileSync(kbPath, "utf8").trim();
18786
+ if (fs28.existsSync(kbPath)) {
18787
+ const content = fs28.readFileSync(kbPath, "utf8").trim();
18452
18788
  if (content) {
18453
18789
  try {
18454
18790
  bindings = parseJsonc(content);
@@ -18468,7 +18804,7 @@ function App({ args = [] }) {
18468
18804
  },
18469
18805
  "when": "terminalFocus"
18470
18806
  });
18471
- fs27.writeFileSync(kbPath, JSON.stringify(bindings, null, 4), "utf8");
18807
+ fs28.writeFileSync(kbPath, JSON.stringify(bindings, null, 4), "utf8");
18472
18808
  cachedShortcut = "Shift + Enter";
18473
18809
  setMessages((prev) => {
18474
18810
  setCompletedIndex(prev.length + 1);
@@ -19179,7 +19515,7 @@ function App({ args = [] }) {
19179
19515
  useEffect12(() => {
19180
19516
  async function init() {
19181
19517
  try {
19182
- const pkg = JSON.parse(fs27.readFileSync(path25.join(process.cwd(), "package.json"), "utf8"));
19518
+ const pkg = JSON.parse(fs28.readFileSync(path26.join(process.cwd(), "package.json"), "utf8"));
19183
19519
  initBridge(versionFluxflow || pkg.version || "2.0.0");
19184
19520
  } catch (e) {
19185
19521
  initBridge("2.0.0");
@@ -19293,7 +19629,7 @@ function App({ args = [] }) {
19293
19629
  if (!parsedArgs.playground) {
19294
19630
  deleteChat(PLAYGROUND_CHAT_ID).catch(() => {
19295
19631
  });
19296
- fs27.remove(path25.join(DATA_DIR, "playground")).catch(() => {
19632
+ fs28.remove(path26.join(DATA_DIR, "playground")).catch(() => {
19297
19633
  });
19298
19634
  }
19299
19635
  performVersionCheck(false, freshSettings);
@@ -19327,9 +19663,9 @@ function App({ args = [] }) {
19327
19663
  }
19328
19664
  }
19329
19665
  if (parsedArgs.playground) {
19330
- const playgroundDir = path25.join(DATA_DIR, "playground");
19666
+ const playgroundDir = path26.join(DATA_DIR, "playground");
19331
19667
  try {
19332
- fs27.ensureDirSync(playgroundDir);
19668
+ fs28.ensureDirSync(playgroundDir);
19333
19669
  process.chdir(playgroundDir);
19334
19670
  } catch (e) {
19335
19671
  }
@@ -19370,8 +19706,8 @@ function App({ args = [] }) {
19370
19706
  if (kbPath) {
19371
19707
  try {
19372
19708
  let bindings = [];
19373
- if (fs27.existsSync(kbPath)) {
19374
- const content = fs27.readFileSync(kbPath, "utf8").trim();
19709
+ if (fs28.existsSync(kbPath)) {
19710
+ const content = fs28.readFileSync(kbPath, "utf8").trim();
19375
19711
  if (content) {
19376
19712
  bindings = parseJsonc(content);
19377
19713
  }
@@ -19527,7 +19863,14 @@ function App({ args = [] }) {
19527
19863
  { cmd: "/revert", desc: "Revert codebase back to a checkpoint" },
19528
19864
  { cmd: "/gemini", desc: "Get a happy message from Gemini CLI" },
19529
19865
  { cmd: "/save", desc: "Force save current chat" },
19530
- { cmd: "/export", desc: "Export current chat in a .txt file" },
19866
+ {
19867
+ cmd: "/export",
19868
+ desc: "Export current chat or error logs",
19869
+ subs: [
19870
+ { cmd: "chat", desc: "Export current active chat" },
19871
+ { cmd: "logs", desc: "Export error logs" }
19872
+ ]
19873
+ },
19531
19874
  { cmd: "/chats", desc: "List all chat sessions" },
19532
19875
  { cmd: "/btw", desc: "Ask a question without intefering with ongoing tasks" },
19533
19876
  {
@@ -19742,22 +20085,22 @@ ${cleanText}`, color: "magenta" }];
19742
20085
  });
19743
20086
  break;
19744
20087
  }
19745
- const src = path25.join(DATA_DIR, "playground");
19746
- const dest = path25.join(parsedArgs.originalCwd, "playground-export");
20088
+ const src = path26.join(DATA_DIR, "playground");
20089
+ const dest = path26.join(parsedArgs.originalCwd, "playground-export");
19747
20090
  const moveFiles = async () => {
19748
20091
  try {
19749
20092
  setMessages((prev) => {
19750
20093
  setCompletedIndex(prev.length + 1);
19751
20094
  return [...prev, { id: Date.now(), role: "system", text: `[PLAYGROUND] Exporting playground content to ${dest}`, isMeta: true }];
19752
20095
  });
19753
- await fs27.ensureDir(dest);
20096
+ await fs28.ensureDir(dest);
19754
20097
  const excludeDirs = ["node_modules", ".git", ".venv", "venv", "env", ".next", "dist", "build", ".cache"];
19755
- await fs27.copy(src, dest, {
20098
+ await fs28.copy(src, dest, {
19756
20099
  overwrite: true,
19757
20100
  filter: (srcPath) => {
19758
- const relative = path25.relative(src, srcPath);
20101
+ const relative = path26.relative(src, srcPath);
19759
20102
  if (!relative) return true;
19760
- const parts2 = relative.split(path25.sep);
20103
+ const parts2 = relative.split(path26.sep);
19761
20104
  return !parts2.some((part) => excludeDirs.includes(part));
19762
20105
  }
19763
20106
  });
@@ -19819,7 +20162,7 @@ ${cleanText}`, color: "magenta" }];
19819
20162
  }
19820
20163
  }
19821
20164
  setTimeout(() => {
19822
- fs27.emptyDir(path25.join(DATA_DIR, "playground")).catch((err) => {
20165
+ fs28.emptyDir(path26.join(DATA_DIR, "playground")).catch((err) => {
19823
20166
  setMessages((prev) => {
19824
20167
  const newMsgs = [...prev, {
19825
20168
  id: "playground-" + Date.now(),
@@ -20158,80 +20501,31 @@ ${cleanText}`, color: "magenta" }];
20158
20501
  break;
20159
20502
  }
20160
20503
  case "/export": {
20161
- const exportFile = `export-fluxflow-${chatId}.txt`;
20162
- const exportPath = path25.join(process.cwd(), exportFile);
20163
- const exportLines = [];
20164
- let insideAgentBlock = false;
20165
- for (let i = 0; i < messages.length; i++) {
20166
- const msg = messages[i];
20167
- if (!msg) continue;
20168
- if (msg.role === "system" || msg.isMeta || msg.isLogo || String(msg.id).startsWith("welcome")) {
20169
- continue;
20170
- }
20171
- if (msg.role === "user") {
20172
- let cleanUserText = msg.text || "";
20173
- cleanUserText = cleanUserText.replace(/\s*\[Prompted on:.*?\]/g, "").trim();
20174
- if (exportLines.length > 0) {
20175
- exportLines.push("");
20176
- }
20177
- exportLines.push("[USER]");
20178
- exportLines.push(cleanUserText);
20179
- insideAgentBlock = false;
20180
- } else if (msg.role === "think") {
20181
- if (!insideAgentBlock) {
20182
- exportLines.push("");
20183
- exportLines.push("[AGENT]");
20184
- insideAgentBlock = true;
20185
- }
20186
- const cleanThinkText = (msg.text || "").replace(/\[\[\s*turn\s*:\s*(continue|finish)\s*\]\]/gi, "").replace(/\[\[END\]\]/gi, "").replace(/\[\[TOOL RESULTS\]\]/gi, "").replace(/\[TOOL RESULTS\]/gi, "").replace(/\[TOOL RESULT\]/gi, "").trim();
20187
- if (cleanThinkText) {
20188
- exportLines.push("[thoughts]");
20189
- exportLines.push(cleanThinkText);
20190
- }
20191
- } else if (msg.role === "agent") {
20192
- if (!insideAgentBlock) {
20193
- exportLines.push("");
20194
- exportLines.push("[AGENT]");
20195
- insideAgentBlock = true;
20196
- }
20197
- const blocks = parseAgentText(msg.text || "");
20198
- for (const block of blocks) {
20199
- if (block.type === "output") {
20200
- const cleanContent = block.content.replace(/\[\[\s*turn\s*:\s*(continue|finish)\s*\]\]/gi, "").replace(/\[\[END\]\]/gi, "").replace(/\[\[TOOL RESULTS\]\]/gi, "").replace(/\[TOOL RESULTS\]/gi, "").replace(/\[TOOL RESULT\]/gi, "").trim();
20201
- if (cleanContent) {
20202
- exportLines.push("[output]");
20203
- exportLines.push(cleanContent);
20204
- }
20205
- } else if (block.type === "tool") {
20206
- exportLines.push("[tool]");
20207
- exportLines.push(`${block.toolName} ${block.args}`);
20208
- }
20209
- }
20504
+ const runExport = async () => {
20505
+ try {
20506
+ const result = await handleExport(parts, { chatId, messages });
20507
+ setMessages((prev) => {
20508
+ setCompletedIndex(prev.length + 1);
20509
+ return [...prev, {
20510
+ id: Date.now(),
20511
+ role: "system",
20512
+ text: result.message,
20513
+ isMeta: true
20514
+ }];
20515
+ });
20516
+ } catch (err) {
20517
+ setMessages((prev) => {
20518
+ setCompletedIndex(prev.length + 1);
20519
+ return [...prev, {
20520
+ id: Date.now(),
20521
+ role: "system",
20522
+ text: `[EXPORT ERROR] Failed to export: ${err.message}`,
20523
+ isMeta: true
20524
+ }];
20525
+ });
20210
20526
  }
20211
- }
20212
- const fileContent = exportLines.join("\n");
20213
- try {
20214
- fs27.writeFileSync(exportPath, fileContent, "utf8");
20215
- setMessages((prev) => {
20216
- setCompletedIndex(prev.length + 1);
20217
- return [...prev, {
20218
- id: Date.now(),
20219
- role: "system",
20220
- text: `[EXPORT] Chat exported to "${exportFile}"`,
20221
- isMeta: true
20222
- }];
20223
- });
20224
- } catch (err) {
20225
- setMessages((prev) => {
20226
- setCompletedIndex(prev.length + 1);
20227
- return [...prev, {
20228
- id: Date.now(),
20229
- role: "system",
20230
- text: `[EXPORT ERROR] Failed to export chat: ${err.message}`,
20231
- isMeta: true
20232
- }];
20233
- });
20234
- }
20527
+ };
20528
+ runExport();
20235
20529
  break;
20236
20530
  }
20237
20531
  case "/chats": {
@@ -20258,12 +20552,12 @@ ${list || "No saved chats found."}`, isMeta: true }];
20258
20552
  setCompletedIndex(prev.length + 1);
20259
20553
  return [...prev, { id: Date.now(), role: "system", text: "[NUCLEAR] Initiating reset...", isMeta: true }];
20260
20554
  });
20261
- if (fs27.existsSync(LOGS_DIR)) fs27.removeSync(LOGS_DIR);
20262
- if (fs27.existsSync(SECRET_DIR)) fs27.removeSync(SECRET_DIR);
20263
- if (fs27.existsSync(SETTINGS_FILE)) fs27.removeSync(SETTINGS_FILE);
20555
+ if (fs28.existsSync(LOGS_DIR)) fs28.removeSync(LOGS_DIR);
20556
+ if (fs28.existsSync(SECRET_DIR)) fs28.removeSync(SECRET_DIR);
20557
+ if (fs28.existsSync(SETTINGS_FILE)) fs28.removeSync(SETTINGS_FILE);
20264
20558
  try {
20265
- const items = fs27.readdirSync(FLUXFLOW_DIR);
20266
- if (items.length === 0) fs27.removeSync(FLUXFLOW_DIR);
20559
+ const items = fs28.readdirSync(FLUXFLOW_DIR);
20560
+ if (items.length === 0) fs28.removeSync(FLUXFLOW_DIR);
20267
20561
  } catch (e) {
20268
20562
  }
20269
20563
  setTimeout(() => {
@@ -20385,15 +20679,15 @@ ${list || "No saved chats found."}`, isMeta: true }];
20385
20679
  # SKILLS & WORKFLOWS
20386
20680
  - [Define custom step-by-step recipes for this project here]
20387
20681
  `;
20388
- const filePath = path25.join(process.cwd(), "FluxFlow.md");
20389
- if (fs27.pathExistsSync(filePath)) {
20682
+ const filePath = path26.join(process.cwd(), "FluxFlow.md");
20683
+ if (fs28.pathExistsSync(filePath)) {
20390
20684
  setMessages((prev) => {
20391
20685
  setCompletedIndex(prev.length + 1);
20392
20686
  return [...prev, { id: "init-err-" + Date.now(), role: "system", text: "ERROR: FluxFlow.md already exists in this directory.", isMeta: true }];
20393
20687
  });
20394
20688
  } else {
20395
20689
  try {
20396
- fs27.writeFileSync(filePath, template);
20690
+ fs28.writeFileSync(filePath, template);
20397
20691
  setMessages((prev) => {
20398
20692
  setCompletedIndex(prev.length + 1);
20399
20693
  return [...prev, { id: "init-ok-" + Date.now(), role: "system", text: "[SUCCESS] FluxFlow.md has been initialized. You can now customize it for this project.", isMeta: true }];
@@ -22148,7 +22442,7 @@ Selection: ${val}`,
22148
22442
  initialData: profileData,
22149
22443
  onSave: (profile) => {
22150
22444
  setProfileData(profile);
22151
- setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: `Profile updated: ${profile.name} (${profile.nickname})` }]);
22445
+ setMessages((prev) => [...prev, { id: Date.now(), role: "system", text: `${profile.name.length > 0 || profile.nickname.length > 0 ? `Profile Updated: ${profile.name.length > 0 ? `${profile.name} ` : ""}${profile.nickname.length > 0 ? `(${profile.nickname})` : ""}` : "Profile: Nothing to Update"}`, isMeta: true }]);
22152
22446
  setActiveView("chat");
22153
22447
  },
22154
22448
  onCancel: () => setActiveView("chat"),
@@ -22209,7 +22503,7 @@ Selection: ${val}`,
22209
22503
  }
22210
22504
  const newVal = args2.content || args2.ReplacementContent || args2.content_to_add || args2.replacementContent || args2.newContent || null;
22211
22505
  return /* @__PURE__ */ React16.createElement(Text16, { color: "white", wrap: "anywhere" }, (newVal ? newVal.replace(/\[\/n\]?/g, "\\n") : null) || "Updating file content...");
22212
- })()) : /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: "cyan", italic: true }, "\u26A1\uFE0F FluxFlow Companion is active. Review the changes in your editor.")), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(
22506
+ })()) : /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: "cyan", italic: true }, "FluxFlow Companion is active. Review the changes in your editor.")), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(
22213
22507
  CommandMenu,
22214
22508
  {
22215
22509
  title: "Action Required",
@@ -22546,7 +22840,7 @@ Selection: ${val}`,
22546
22840
  return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", paddingX: 3, paddingY: 1, borderColor: colors.borderMuted, width: Math.min(100, (stdout?.columns || 100) - 2), marginTop: 0, marginBottom: 0 }, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text16, { bold: true }, gradient2(colors.logoGradient || ["blue", "purple"])("Agent powering down. Goodbye!"))), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "Interaction Summary"), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Session ID:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, chatId)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Tool Calls:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, runtimeSession.toolSuccess + runtimeSession.toolFailure + runtimeSession.toolDenied, " ( ", /* @__PURE__ */ React16.createElement(Text16, { color: "green" }, "\u2714 ", runtimeSession.toolSuccess), " ", /* @__PURE__ */ React16.createElement(Text16, { color: "yellow" }, "\u{1F6C7} ", runtimeSession.toolDenied), " ", /* @__PURE__ */ React16.createElement(Text16, { color: "red" }, "\u2718 ", runtimeSession.toolFailure), " )")), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Success Rate:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, successRate, "%")), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Code Changes:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, /* @__PURE__ */ React16.createElement(Text16, { color: "green" }, "+", runtimeSession.linesAdded), " ", /* @__PURE__ */ React16.createElement(Text16, { color: "red" }, "-", runtimeSession.linesRemoved))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Tokens Consumed:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalTokens))), sessionTotalTokens > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 18 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Input Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalTokens - sessionTotalCandidateTokens))), sessionTotalCachedTokens > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Box14, { width: 16 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Cached:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalCachedTokens))), sessionTotalCandidateTokens > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 18 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Output Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalCandidateTokens)))), sessionImageCount > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Images Made:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, sessionImageCount)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Image Credits:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, Number(((sessionImageCredits || 0) * 1e3).toFixed(0)), " credits")))), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "Performance"), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Wall Time:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(wallTimeMs))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 20 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Agent Active:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(agentActiveMs))), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 18 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB API Time:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(sessionApiTime), " (", apiPercent, "%)")), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 18 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Tool Time:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(sessionToolTime), " (", toolPercent, "%)"))));
22547
22841
  })())));
22548
22842
  }
22549
- var shouldClearValue, getPrefilledValue, getIDEName, getIDEDirName, getKeybindingsPath, parseJsonc, hasShiftEnterBinding, getPromoOptions, BridgePromo, SESSION_START_TIME, CHANGELOG_URL, DOCS_URL, packageJsonPath, packageJson, versionFluxflow, updatedOn, ResolutionModal, parseAgentText, getProjectFiles, cachedShortcut, getLatencyColor2, SubagentRow;
22843
+ var shouldClearValue, getPrefilledValue, getIDEName, getIDEDirName, getKeybindingsPath, parseJsonc, hasShiftEnterBinding, getPromoOptions, BridgePromo, SESSION_START_TIME, CHANGELOG_URL, DOCS_URL, packageJsonPath, packageJson, versionFluxflow, updatedOn, ResolutionModal, getProjectFiles, cachedShortcut, getLatencyColor2, SubagentRow;
22550
22844
  var init_app = __esm({
22551
22845
  async "src/app.jsx"() {
22552
22846
  init_build();
@@ -22582,6 +22876,7 @@ var init_app = __esm({
22582
22876
  init_text();
22583
22877
  init_editor();
22584
22878
  init_GlintText();
22879
+ init_export();
22585
22880
  shouldClearValue = (val) => {
22586
22881
  const s = String(val);
22587
22882
  return s.startsWith("999") && s.endsWith("9");
@@ -22632,11 +22927,11 @@ var init_app = __esm({
22632
22927
  if (process.platform === "win32") {
22633
22928
  const appData = process.env.APPDATA;
22634
22929
  if (!appData) return null;
22635
- return path25.join(appData, dirName, "User", "keybindings.json");
22930
+ return path26.join(appData, dirName, "User", "keybindings.json");
22636
22931
  } else if (process.platform === "darwin") {
22637
- return path25.join(home, "Library", "Application Support", dirName, "User", "keybindings.json");
22932
+ return path26.join(home, "Library", "Application Support", dirName, "User", "keybindings.json");
22638
22933
  } else {
22639
- return path25.join(home, ".config", dirName, "User", "keybindings.json");
22934
+ return path26.join(home, ".config", dirName, "User", "keybindings.json");
22640
22935
  }
22641
22936
  };
22642
22937
  parseJsonc = (content) => {
@@ -22680,8 +22975,8 @@ var init_app = __esm({
22680
22975
  SESSION_START_TIME = Date.now();
22681
22976
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog";
22682
22977
  DOCS_URL = "https://fluxflow-cli.onrender.com/";
22683
- packageJsonPath = path25.join(path25.dirname(fileURLToPath3(import.meta.url)), "../package.json");
22684
- packageJson = JSON.parse(fs27.readFileSync(packageJsonPath, "utf8"));
22978
+ packageJsonPath = path26.join(path26.dirname(fileURLToPath3(import.meta.url)), "../package.json");
22979
+ packageJson = JSON.parse(fs28.readFileSync(packageJsonPath, "utf8"));
22685
22980
  versionFluxflow = packageJson.version;
22686
22981
  updatedOn = packageJson.date || "2026-05-20";
22687
22982
  ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "grey", padding: 0, width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: "white", bold: true, underline: true }, data.startsWith("/btw") ? "QUESTION" : "STEERING HINT", " RESOLUTION")), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, null, "The agent already finished the task before your ", data.startsWith("/btw") ? "question" : "hint", " was consumed.")), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1, backgroundColor: "#222", paddingX: 2, width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, { italic: true, color: "gray" }, '"', data.replace("/btw", "").trim(), '"')), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: "grey" }, "How would you like to proceed?")), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 0 }, /* @__PURE__ */ React16.createElement(
@@ -22698,74 +22993,6 @@ var init_app = __esm({
22698
22993
  }
22699
22994
  }
22700
22995
  )));
22701
- parseAgentText = (text) => {
22702
- const blocks = [];
22703
- const toolRegex = /\[\s*(?:tool:functions\.|agent:generalist\.)([a-z0-9_]+)\s*\(/gi;
22704
- let lastIdx = 0;
22705
- let match;
22706
- while ((match = toolRegex.exec(text)) !== null) {
22707
- const toolName = match[1];
22708
- const startIdx = match.index + match[0].length - 1;
22709
- let balance = 0;
22710
- let inString = null;
22711
- let endIdx = -1;
22712
- let closingParenIdx = -1;
22713
- for (let i = startIdx; i < text.length; i++) {
22714
- const char = text[i];
22715
- if (inString) {
22716
- if (char === inString) {
22717
- let backslashCount = 0;
22718
- for (let j = i - 1; j >= 0 && text[j] === "\\"; j--) {
22719
- backslashCount++;
22720
- }
22721
- if (backslashCount % 2 === 0) {
22722
- inString = null;
22723
- }
22724
- }
22725
- } else {
22726
- if (char === '"' || char === "'" || char === "`") {
22727
- inString = char;
22728
- } else if (char === "(") {
22729
- balance++;
22730
- } else if (char === ")") {
22731
- balance--;
22732
- if (balance === 0) {
22733
- closingParenIdx = i;
22734
- let j = i + 1;
22735
- while (j < text.length && /\s/.test(text[j])) j++;
22736
- if (j < text.length && text[j] === "]") {
22737
- endIdx = j;
22738
- break;
22739
- }
22740
- }
22741
- }
22742
- }
22743
- }
22744
- if (endIdx !== -1) {
22745
- const beforeText = flattenString(text.substring(lastIdx, match.index));
22746
- if (beforeText.trim()) {
22747
- blocks.push({ type: "output", content: beforeText });
22748
- }
22749
- const finalArgsText = flattenString(text.substring(startIdx + 1, closingParenIdx));
22750
- blocks.push({
22751
- type: "tool",
22752
- toolName: flattenString(toolName.trim()),
22753
- args: flattenString(finalArgsText.trim())
22754
- });
22755
- lastIdx = endIdx + 1;
22756
- toolRegex.lastIndex = lastIdx;
22757
- } else {
22758
- break;
22759
- }
22760
- }
22761
- if (lastIdx < text.length) {
22762
- const remainingText = flattenString(text.substring(lastIdx));
22763
- if (remainingText.trim()) {
22764
- blocks.push({ type: "output", content: remainingText });
22765
- }
22766
- }
22767
- return blocks;
22768
- };
22769
22996
  getProjectFiles = /* @__PURE__ */ (() => {
22770
22997
  let cachedFiles = null;
22771
22998
  let lastScanTime = 0;
@@ -22778,20 +23005,20 @@ var init_app = __esm({
22778
23005
  const scan = (currentDir) => {
22779
23006
  if (fileList.length >= 2e3) return;
22780
23007
  try {
22781
- const files = fs27.readdirSync(currentDir);
23008
+ const files = fs28.readdirSync(currentDir);
22782
23009
  for (const file of files) {
22783
23010
  if (fileList.length >= 2e3) return;
22784
23011
  if (["node_modules", ".git", ".gemini", "dist", "build", ".next", ".cache", "out"].includes(file)) {
22785
23012
  continue;
22786
23013
  }
22787
- const filePath = path25.join(currentDir, file);
22788
- const stat = fs27.statSync(filePath);
23014
+ const filePath = path26.join(currentDir, file);
23015
+ const stat = fs28.statSync(filePath);
22789
23016
  if (stat.isDirectory()) {
22790
23017
  scan(filePath);
22791
23018
  } else {
22792
23019
  fileList.push({
22793
23020
  name: flattenString(file),
22794
- relativePath: flattenString(path25.relative(process.cwd(), filePath))
23021
+ relativePath: flattenString(path26.relative(process.cwd(), filePath))
22795
23022
  });
22796
23023
  }
22797
23024
  }
@@ -22989,13 +23216,32 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
22989
23216
  const isHelp = args.includes("--help") && !isHelpCommands;
22990
23217
  const isVersion = args.includes("--version") || args.includes("-v");
22991
23218
  const isUpdate = args[0] === "--update";
22992
- if (isVersion || isHelp || isHelpCommands || isUpdate) {
22993
- const fs28 = await import("fs");
22994
- const path26 = await import("path");
23219
+ const isExport = args[0] === "--export";
23220
+ if (isVersion || isHelp || isHelpCommands || isUpdate || isExport) {
23221
+ const fs29 = await import("fs");
23222
+ const path27 = await import("path");
22995
23223
  const { fileURLToPath: fileURLToPath5 } = await import("url");
22996
- const packageJsonPath2 = path26.join(path26.dirname(fileURLToPath5(import.meta.url)), "../package.json");
22997
- const packageJson2 = JSON.parse(fs28.readFileSync(packageJsonPath2, "utf8"));
23224
+ const packageJsonPath2 = path27.join(path27.dirname(fileURLToPath5(import.meta.url)), "../package.json");
23225
+ const packageJson2 = JSON.parse(fs29.readFileSync(packageJsonPath2, "utf8"));
22998
23226
  const versionFluxflow2 = packageJson2.version;
23227
+ if (isExport) {
23228
+ const subArg = (args[1] || "").toLowerCase();
23229
+ if (subArg === "error" || subArg === "logs") {
23230
+ try {
23231
+ const { exportErrorLogs: exportErrorLogs2 } = await Promise.resolve().then(() => (init_export(), export_exports));
23232
+ const result = await exportErrorLogs2();
23233
+ console.log(`[EXPORT LOGS] Exported ${result.entryCount} error log entries (FluxFlow: ${result.fluxflowCount}, Memory: ${result.memoryCount}) to "${result.exportFile}"`);
23234
+ process.exit(0);
23235
+ } catch (err) {
23236
+ console.error(`[EXPORT ERROR] Failed to export error logs: ${err.message}`);
23237
+ process.exit(1);
23238
+ }
23239
+ } else {
23240
+ console.error(`[EXPORT ERROR] Invalid export target "${args[1] || ""}". --export only supports 'error'.
23241
+ Usage: fluxflow --export error`);
23242
+ process.exit(1);
23243
+ }
23244
+ }
22999
23245
  if (isVersion) {
23000
23246
  console.log(`v${versionFluxflow2}`);
23001
23247
  process.exit(0);
@@ -23019,6 +23265,7 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
23019
23265
  --help Show this help menu
23020
23266
  --help commands Show available /commands
23021
23267
  --playground Launch in Playground mode (fixed session, CWD: DATA_DIR/playground)
23268
+ --export error Export system error logs to fluxflow-error-<timestamp>.txt
23022
23269
  --update check Check for new updates
23023
23270
  --update check latest Show the latest version available on npm
23024
23271
  --update [latest] Update the app to the latest version (latest is default)`);
@@ -23033,7 +23280,7 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
23033
23280
  /compress Summarize and compress chat history
23034
23281
  /revert Revert codebase back to a checkpoint
23035
23282
  /save Force save current chat
23036
- /export Export current chat in a .txt file
23283
+ /export [chat|logs] Export chat session or system error logs
23037
23284
  /chats List all chat sessions
23038
23285
  /btw <question> Send raw inquiry to the agent mid-turn
23039
23286
  /image setup key <default|custom> Configure image API key strategy