@bike4mind/cli 0.2.1-quest-file-uploads.17007 → 0.2.3-basic-cli-shortcuts.17058

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-QXGU6KYI.js";
8
- import "./chunk-LZMRYJ3B.js";
9
- import "./chunk-F3ETXO5N.js";
7
+ } from "./chunk-GZTILMGE.js";
8
+ import "./chunk-Y4HMTEDH.js";
9
+ import "./chunk-5N4PG5EH.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,7 +15,7 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-U3GPYBTD.js";
18
+ } from "./chunk-NZADYDFG.js";
19
19
  import {
20
20
  Logger
21
21
  } from "./chunk-AMDXHL6S.js";
@@ -29,6 +29,7 @@ import {
29
29
  AuthEvents,
30
30
  BFL_IMAGE_MODELS,
31
31
  BFL_SAFETY_TOLERANCE,
32
+ CREDIT_DEDUCT_TRANSACTION_TYPES,
32
33
  ChatCompletionCreateInputSchema,
33
34
  ChatModels,
34
35
  CollectionType,
@@ -78,13 +79,13 @@ import {
78
79
  } from "./chunk-PDX44BCA.js";
79
80
 
80
81
  // src/index.tsx
81
- import React10, { useState as useState3, useEffect as useEffect3, useCallback } from "react";
82
- import { render, Box as Box10, Text as Text9, useApp, useInput as useInput2 } from "ink";
82
+ import React12, { useState as useState4, useEffect as useEffect3, useCallback } from "react";
83
+ import { render, Box as Box11, Text as Text11, useApp, useInput as useInput3 } from "ink";
83
84
  import { v4 as uuidv49 } from "uuid";
84
85
 
85
86
  // src/components/App.tsx
86
- import React7 from "react";
87
- import { Box as Box7, Text as Text6, Static } from "ink";
87
+ import React9 from "react";
88
+ import { Box as Box8, Text as Text8, Static } from "ink";
88
89
 
89
90
  // src/components/StatusBar.tsx
90
91
  import React from "react";
@@ -94,26 +95,327 @@ var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage
94
95
  });
95
96
 
96
97
  // src/components/InputPrompt.tsx
97
- import React3, { useState, useMemo, useEffect } from "react";
98
- import { Box as Box3, Text as Text3, useInput } from "ink";
99
- import TextInput from "ink-text-input";
98
+ import React5, { useState as useState2, useMemo, useEffect } from "react";
99
+ import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
100
+
101
+ // src/components/CustomTextInput.tsx
102
+ import React2, { useState } from "react";
103
+ import { Text as Text2, useInput } from "ink";
104
+ function CustomTextInput({
105
+ value,
106
+ onChange,
107
+ onSubmit,
108
+ placeholder = "",
109
+ showCursor = true
110
+ }) {
111
+ const [cursorOffset, setCursorOffset] = useState(value.length);
112
+ useInput(
113
+ (input, key) => {
114
+ if (key.return) {
115
+ onSubmit(value);
116
+ return;
117
+ }
118
+ if (key.ctrl) {
119
+ switch (input) {
120
+ case "u":
121
+ {
122
+ const lines = value.split("\n");
123
+ let currentLineStart = 0;
124
+ let currentLineIndex = 0;
125
+ let charCount = 0;
126
+ for (let i = 0; i < lines.length; i++) {
127
+ const lineLength = lines[i].length;
128
+ if (charCount + lineLength >= cursorOffset) {
129
+ currentLineIndex = i;
130
+ currentLineStart = charCount;
131
+ break;
132
+ }
133
+ charCount += lineLength + 1;
134
+ }
135
+ lines.splice(currentLineIndex, 1);
136
+ const newValue = lines.join("\n");
137
+ onChange(newValue);
138
+ setCursorOffset(Math.min(currentLineStart, newValue.length));
139
+ }
140
+ return;
141
+ case "k":
142
+ onChange(value.slice(0, cursorOffset));
143
+ return;
144
+ case "w":
145
+ {
146
+ const beforeCursor2 = value.slice(0, cursorOffset);
147
+ const afterCursor2 = value.slice(cursorOffset);
148
+ const trimmed = beforeCursor2.trimEnd();
149
+ const lastSpace = trimmed.lastIndexOf(" ");
150
+ const newBefore = lastSpace === -1 ? "" : trimmed.slice(0, lastSpace + 1);
151
+ const newValue = newBefore + afterCursor2;
152
+ onChange(newValue);
153
+ setCursorOffset(newBefore.length);
154
+ }
155
+ return;
156
+ case "a":
157
+ setCursorOffset(0);
158
+ return;
159
+ case "e":
160
+ setCursorOffset(value.length);
161
+ return;
162
+ case "l":
163
+ onChange("");
164
+ return;
165
+ }
166
+ }
167
+ if (key.backspace || key.delete) {
168
+ if (cursorOffset > 0) {
169
+ const newValue = value.slice(0, cursorOffset - 1) + value.slice(cursorOffset);
170
+ onChange(newValue);
171
+ setCursorOffset(cursorOffset - 1);
172
+ }
173
+ return;
174
+ }
175
+ if (key.leftArrow) {
176
+ setCursorOffset(Math.max(0, cursorOffset - 1));
177
+ return;
178
+ }
179
+ if (key.rightArrow) {
180
+ setCursorOffset(Math.min(value.length, cursorOffset + 1));
181
+ return;
182
+ }
183
+ if (!key.ctrl && !key.meta && input.length === 1) {
184
+ const newValue = value.slice(0, cursorOffset) + input + value.slice(cursorOffset);
185
+ onChange(newValue);
186
+ setCursorOffset(cursorOffset + 1);
187
+ }
188
+ },
189
+ { isActive: true }
190
+ );
191
+ const hasValue = value.length > 0;
192
+ if (!hasValue) {
193
+ return /* @__PURE__ */ React2.createElement(Text2, null, showCursor ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, " "), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder)) : /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder));
194
+ }
195
+ const beforeCursor = value.slice(0, cursorOffset);
196
+ const cursorChar = value[cursorOffset] || " ";
197
+ const afterCursor = value.slice(cursorOffset + 1);
198
+ return /* @__PURE__ */ React2.createElement(Text2, null, beforeCursor, showCursor && /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, cursorChar), !showCursor && cursorChar, afterCursor);
199
+ }
100
200
 
101
201
  // src/components/CommandAutocomplete.tsx
102
- import React2 from "react";
103
- import { Box as Box2, Text as Text2 } from "ink";
202
+ import React3 from "react";
203
+ import { Box as Box2, Text as Text3 } from "ink";
104
204
  function CommandAutocomplete({ commands, selectedIndex }) {
105
205
  if (commands.length === 0) {
106
- return /* @__PURE__ */ React2.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "No matching commands"));
206
+ return /* @__PURE__ */ React3.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching commands"));
107
207
  }
108
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, commands.length === 1 ? "1 command" : `${commands.length} commands`, " \u2022 Use \u2191\u2193 to navigate, Enter to select")), commands.map((cmd, index) => {
208
+ return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, commands.length === 1 ? "1 command" : `${commands.length} commands`, " \u2022 Use \u2191\u2193 to navigate, Enter to select")), commands.map((cmd, index) => {
109
209
  const args = cmd.args ? ` ${cmd.args}` : "";
110
210
  const isSelected = index === selectedIndex;
111
- return /* @__PURE__ */ React2.createElement(Box2, { key: cmd.name, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "\u25B8 " : " ", "/", cmd.name, args, " - ", cmd.description));
211
+ return /* @__PURE__ */ React3.createElement(Box2, { key: cmd.name, marginLeft: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "\u25B8 " : " ", "/", cmd.name, args, " - ", cmd.description));
112
212
  }));
113
213
  }
114
214
 
115
- // src/utils/fuzzySearch.ts
215
+ // src/components/FileAutocomplete.tsx
216
+ import React4 from "react";
217
+ import { Box as Box3, Text as Text4 } from "ink";
218
+
219
+ // src/utils/fileSearch.ts
220
+ import * as fs from "fs";
221
+ import * as path from "path";
116
222
  import Fuse from "fuse.js";
223
+ var DEFAULT_IGNORE_PATTERNS = [
224
+ "node_modules",
225
+ ".git",
226
+ ".next",
227
+ ".turbo",
228
+ "dist",
229
+ "build",
230
+ ".cache",
231
+ "coverage",
232
+ ".DS_Store",
233
+ ".env",
234
+ ".env.local",
235
+ ".env.secrets"
236
+ ];
237
+ function walkDirectory(basePath = process.cwd(), maxDepth = 5, ignorePatterns = DEFAULT_IGNORE_PATTERNS) {
238
+ const results = [];
239
+ const normalizedBase = path.resolve(basePath);
240
+ function walk(currentPath, depth) {
241
+ if (depth > maxDepth) return;
242
+ try {
243
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true });
244
+ for (const entry of entries) {
245
+ if (ignorePatterns.some((pattern) => entry.name === pattern || entry.name.startsWith(pattern))) {
246
+ continue;
247
+ }
248
+ const fullPath = path.join(currentPath, entry.name);
249
+ const relativePath = path.relative(normalizedBase, fullPath);
250
+ if (entry.isDirectory()) {
251
+ results.push({
252
+ path: relativePath,
253
+ isDirectory: true
254
+ });
255
+ walk(fullPath, depth + 1);
256
+ } else if (entry.isFile()) {
257
+ try {
258
+ const stats = fs.statSync(fullPath);
259
+ results.push({
260
+ path: relativePath,
261
+ isDirectory: false,
262
+ size: stats.size
263
+ });
264
+ } catch {
265
+ results.push({
266
+ path: relativePath,
267
+ isDirectory: false
268
+ });
269
+ }
270
+ }
271
+ }
272
+ } catch {
273
+ }
274
+ }
275
+ walk(normalizedBase, 0);
276
+ return results;
277
+ }
278
+ function formatFileSize(bytes) {
279
+ if (bytes < 1024) return `${bytes} B`;
280
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
281
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
282
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
283
+ }
284
+ var cachedFiles = null;
285
+ var cacheTimestamp = 0;
286
+ var CACHE_TTL_MS = 3e4;
287
+ function getCachedFiles() {
288
+ const now = Date.now();
289
+ if (!cachedFiles || now - cacheTimestamp > CACHE_TTL_MS) {
290
+ cachedFiles = walkDirectory();
291
+ cacheTimestamp = now;
292
+ }
293
+ return cachedFiles;
294
+ }
295
+ function searchFiles(query) {
296
+ const files = getCachedFiles();
297
+ if (!query || query.trim() === "") {
298
+ return files.filter((f) => !f.path.includes(path.sep)).slice(0, 20);
299
+ }
300
+ const queryDir = path.dirname(query);
301
+ let searchPool = files;
302
+ if (queryDir && queryDir !== ".") {
303
+ const normalizedDir = queryDir.replace(/\\/g, "/");
304
+ searchPool = files.filter((f) => {
305
+ const normalizedPath = f.path.replace(/\\/g, "/");
306
+ return normalizedPath.startsWith(normalizedDir + "/") || normalizedPath === normalizedDir;
307
+ });
308
+ if (searchPool.length === 0) {
309
+ searchPool = files;
310
+ }
311
+ }
312
+ const fuse2 = new Fuse(searchPool, {
313
+ keys: [{ name: "path", weight: 1 }],
314
+ threshold: 0.4,
315
+ // Allow some fuzzy matching
316
+ includeScore: true,
317
+ minMatchCharLength: 1,
318
+ // Use extended search for better path matching
319
+ useExtendedSearch: false
320
+ });
321
+ const results = fuse2.search(query);
322
+ return results.slice(0, 20).map((result) => result.item);
323
+ }
324
+ function isPathWithinCwd(filePath) {
325
+ const cwd = path.resolve(process.cwd());
326
+ const absolutePath = path.resolve(cwd, filePath);
327
+ let resolvedCwd = cwd;
328
+ let resolvedPath = absolutePath;
329
+ try {
330
+ const realCwd = fs.realpathSync.native(cwd);
331
+ if (realCwd) {
332
+ resolvedCwd = realCwd;
333
+ }
334
+ } catch {
335
+ }
336
+ try {
337
+ const realPath = fs.realpathSync.native(absolutePath);
338
+ if (realPath) {
339
+ resolvedPath = realPath;
340
+ }
341
+ } catch {
342
+ const relativePath2 = path.relative(cwd, absolutePath);
343
+ resolvedPath = path.resolve(resolvedCwd, relativePath2);
344
+ }
345
+ const normalizedCwd = resolvedCwd.toLowerCase();
346
+ const normalizedPath = resolvedPath.toLowerCase();
347
+ const isWithin = normalizedPath === normalizedCwd || normalizedPath.startsWith(normalizedCwd + path.sep);
348
+ if (!isWithin) {
349
+ return false;
350
+ }
351
+ const relativePath = path.relative(resolvedCwd, resolvedPath);
352
+ return !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
353
+ }
354
+ function isBinaryFile(filePath) {
355
+ const binaryExtensions = [
356
+ ".png",
357
+ ".jpg",
358
+ ".jpeg",
359
+ ".gif",
360
+ ".bmp",
361
+ ".ico",
362
+ ".webp",
363
+ // Note: .svg is NOT included because SVG is XML text format that can be safely read
364
+ ".pdf",
365
+ ".doc",
366
+ ".docx",
367
+ ".xls",
368
+ ".xlsx",
369
+ ".ppt",
370
+ ".pptx",
371
+ ".zip",
372
+ ".tar",
373
+ ".gz",
374
+ ".rar",
375
+ ".7z",
376
+ ".exe",
377
+ ".dll",
378
+ ".so",
379
+ ".dylib",
380
+ ".bin",
381
+ ".dat",
382
+ ".mp3",
383
+ ".mp4",
384
+ ".wav",
385
+ ".avi",
386
+ ".mov",
387
+ ".mkv",
388
+ ".ttf",
389
+ ".otf",
390
+ ".woff",
391
+ ".woff2",
392
+ ".eot",
393
+ ".class",
394
+ ".pyc",
395
+ ".o",
396
+ ".obj"
397
+ ];
398
+ const ext = path.extname(filePath).toLowerCase();
399
+ return binaryExtensions.includes(ext);
400
+ }
401
+ var MAX_FILE_SIZE = 10 * 1024 * 1024;
402
+
403
+ // src/components/FileAutocomplete.tsx
404
+ function FileAutocomplete({ files, selectedIndex, query }) {
405
+ if (files.length === 0) {
406
+ return /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
407
+ }
408
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, files.length === 1 ? "1 match" : `${files.length} matches`, " - Use up/down to navigate, Tab to select")), files.map((file, index) => {
409
+ const isSelected = index === selectedIndex;
410
+ const icon = file.isDirectory ? "[folder]" : "[file] ";
411
+ const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
412
+ const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
413
+ return /* @__PURE__ */ React4.createElement(Box3, { key: file.path, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "> " : " "), /* @__PURE__ */ React4.createElement(Text4, { color: file.isDirectory ? "yellow" : isSelected ? "cyan" : void 0, bold: isSelected }, icon), /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : void 0, bold: isSelected }, " ", pathDisplay), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, sizeDisplay));
414
+ }));
415
+ }
416
+
417
+ // src/utils/fuzzySearch.ts
418
+ import Fuse2 from "fuse.js";
117
419
 
118
420
  // src/config/commands.ts
119
421
  var COMMANDS = [
@@ -177,11 +479,15 @@ var COMMANDS = [
177
479
  {
178
480
  name: "trusted",
179
481
  description: "List all trusted tools"
482
+ },
483
+ {
484
+ name: "usage",
485
+ description: "Show credit usage and balance"
180
486
  }
181
487
  ];
182
488
 
183
489
  // src/utils/fuzzySearch.ts
184
- var fuse = new Fuse(COMMANDS, {
490
+ var fuse = new Fuse2(COMMANDS, {
185
491
  keys: [
186
492
  { name: "name", weight: 0.7 },
187
493
  // Prioritize command name
@@ -202,23 +508,64 @@ function searchCommands(query) {
202
508
  }
203
509
 
204
510
  // src/components/InputPrompt.tsx
511
+ function findAtTrigger(value) {
512
+ for (let i = value.length - 1; i >= 0; i--) {
513
+ if (value[i] === "@") {
514
+ const isValid = i === 0 || value[i - 1] === " ";
515
+ if (isValid) {
516
+ const query = value.slice(i + 1);
517
+ if (query.includes(" ")) {
518
+ return null;
519
+ }
520
+ return { startIndex: i, query };
521
+ }
522
+ }
523
+ }
524
+ return null;
525
+ }
205
526
  function InputPrompt({ onSubmit, disabled = false, history = [] }) {
206
- const [value, setValue] = useState("");
207
- const [selectedIndex, setSelectedIndex] = useState(0);
208
- const [historyIndex, setHistoryIndex] = useState(-1);
209
- const [tempInput, setTempInput] = useState("");
210
- const [inputKey, setInputKey] = useState(0);
211
- const shouldShowAutocomplete = value.startsWith("/") && !disabled;
212
- const query = shouldShowAutocomplete ? value.slice(1) : "";
527
+ const [value, setValue] = useState2("");
528
+ const [selectedIndex, setSelectedIndex] = useState2(0);
529
+ const [historyIndex, setHistoryIndex] = useState2(-1);
530
+ const [tempInput, setTempInput] = useState2("");
531
+ const [fileAutocomplete, setFileAutocomplete] = useState2(null);
532
+ const [fileSelectedIndex, setFileSelectedIndex] = useState2(0);
533
+ const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
534
+ const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
213
535
  const filteredCommands = useMemo(() => {
214
- if (!shouldShowAutocomplete) return [];
215
- return searchCommands(query);
216
- }, [shouldShowAutocomplete, query]);
536
+ if (!shouldShowCommandAutocomplete) return [];
537
+ return searchCommands(commandQuery);
538
+ }, [shouldShowCommandAutocomplete, commandQuery]);
539
+ const filteredFiles = useMemo(() => {
540
+ if (!fileAutocomplete?.active) return [];
541
+ return searchFiles(fileAutocomplete.query);
542
+ }, [fileAutocomplete?.active, fileAutocomplete?.query]);
217
543
  useEffect(() => {
218
544
  setSelectedIndex(0);
219
545
  }, [filteredCommands]);
220
- useInput((input, key) => {
221
- if (shouldShowAutocomplete && filteredCommands.length > 0) {
546
+ useEffect(() => {
547
+ setFileSelectedIndex(0);
548
+ }, [filteredFiles]);
549
+ useInput2((_input, key) => {
550
+ if (fileAutocomplete?.active && filteredFiles.length > 0) {
551
+ if (key.upArrow) {
552
+ setFileSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredFiles.length - 1);
553
+ return;
554
+ } else if (key.downArrow) {
555
+ setFileSelectedIndex((prev) => prev < filteredFiles.length - 1 ? prev + 1 : 0);
556
+ return;
557
+ } else if (key.tab) {
558
+ const selectedFile = filteredFiles[fileSelectedIndex];
559
+ if (selectedFile) {
560
+ insertSelectedFile(selectedFile);
561
+ }
562
+ return;
563
+ } else if (key.escape) {
564
+ setFileAutocomplete(null);
565
+ return;
566
+ }
567
+ }
568
+ if (shouldShowCommandAutocomplete && filteredCommands.length > 0) {
222
569
  if (key.upArrow) {
223
570
  setSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredCommands.length - 1);
224
571
  } else if (key.downArrow) {
@@ -226,7 +573,7 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
226
573
  }
227
574
  return;
228
575
  }
229
- if (!shouldShowAutocomplete && history.length > 0) {
576
+ if (!shouldShowCommandAutocomplete && !fileAutocomplete?.active && history.length > 0) {
230
577
  if (key.upArrow) {
231
578
  if (historyIndex === -1) {
232
579
  setTempInput(value);
@@ -249,15 +596,39 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
249
596
  }
250
597
  }
251
598
  });
599
+ const insertSelectedFile = (file) => {
600
+ if (!fileAutocomplete) return;
601
+ const beforeAt = value.slice(0, fileAutocomplete.startIndex);
602
+ const afterQuery = value.slice(fileAutocomplete.startIndex + 1 + fileAutocomplete.query.length);
603
+ const pathToInsert = file.isDirectory ? `@${file.path}/` : `@${file.path}`;
604
+ const newValue = beforeAt + pathToInsert + afterQuery;
605
+ setValue(newValue);
606
+ if (file.isDirectory) {
607
+ setFileAutocomplete({
608
+ active: true,
609
+ startIndex: fileAutocomplete.startIndex,
610
+ query: file.path + "/"
611
+ });
612
+ setFileSelectedIndex(0);
613
+ } else {
614
+ setFileAutocomplete(null);
615
+ }
616
+ };
252
617
  const handleSubmit = (input) => {
253
618
  if (disabled || !input.trim()) return;
254
- if (shouldShowAutocomplete && filteredCommands.length > 0) {
619
+ if (fileAutocomplete?.active && filteredFiles.length > 0) {
620
+ const selectedFile = filteredFiles[fileSelectedIndex];
621
+ if (selectedFile) {
622
+ insertSelectedFile(selectedFile);
623
+ }
624
+ return;
625
+ }
626
+ if (shouldShowCommandAutocomplete && filteredCommands.length > 0) {
255
627
  const selectedCommand = filteredCommands[selectedIndex];
256
628
  const commandText = `/${selectedCommand.name}`;
257
629
  if (selectedCommand.args) {
258
630
  setValue(commandText + " ");
259
631
  setHistoryIndex(-1);
260
- setInputKey((prev) => prev + 1);
261
632
  return;
262
633
  }
263
634
  setValue("");
@@ -268,6 +639,7 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
268
639
  onSubmit(input);
269
640
  setValue("");
270
641
  setHistoryIndex(-1);
642
+ setFileAutocomplete(null);
271
643
  };
272
644
  const handleChange = (newValue) => {
273
645
  setValue(newValue);
@@ -275,30 +647,39 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
275
647
  setHistoryIndex(-1);
276
648
  setTempInput("");
277
649
  }
650
+ const atTrigger = findAtTrigger(newValue);
651
+ if (atTrigger) {
652
+ setFileAutocomplete({
653
+ active: true,
654
+ startIndex: atTrigger.startIndex,
655
+ query: atTrigger.query
656
+ });
657
+ } else {
658
+ setFileAutocomplete(null);
659
+ }
278
660
  };
279
- return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React3.createElement(
280
- TextInput,
661
+ return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(
662
+ CustomTextInput,
281
663
  {
282
- key: inputKey,
283
664
  value,
284
665
  onChange: handleChange,
285
666
  onSubmit: handleSubmit,
286
- placeholder: disabled ? "Thinking..." : "Type your message or /help for commands",
667
+ placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
287
668
  showCursor: !disabled
288
669
  }
289
- )), shouldShowAutocomplete && /* @__PURE__ */ React3.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }));
670
+ )), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
290
671
  }
291
672
 
292
673
  // src/components/AgentThinking.tsx
293
- import React5 from "react";
294
- import { Box as Box5 } from "ink";
674
+ import React7 from "react";
675
+ import { Box as Box6 } from "ink";
295
676
 
296
677
  // src/components/ThoughtStream.tsx
297
- import React4 from "react";
298
- import { Box as Box4, Text as Text4 } from "ink";
678
+ import React6 from "react";
679
+ import { Box as Box5, Text as Text6 } from "ink";
299
680
  import Spinner from "ink-spinner";
300
- var ThoughtStream = React4.memo(function ThoughtStream2({ steps }) {
301
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", gap: 1 }, steps.map((step, index) => /* @__PURE__ */ React4.createElement(Box4, { key: index, flexDirection: "column" }, step.type === "thought" && /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "magenta" }, "\u{1F914} Thought: "), /* @__PURE__ */ React4.createElement(Text4, null, step.content)), step.type === "action" && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "yellow" }, "\u26A1 Action: "), /* @__PURE__ */ React4.createElement(Text4, { bold: true }, step.metadata?.toolName || "unknown")), step.metadata?.toolInput && /* @__PURE__ */ React4.createElement(Box4, { paddingLeft: 2 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, JSON.stringify(step.metadata.toolInput, null, 2)))), step.type === "observation" && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, "\u{1F4CA} Observation: ")), /* @__PURE__ */ React4.createElement(Box4, { paddingLeft: 2 }, /* @__PURE__ */ React4.createElement(Text4, null, step.content))), step.type === "final_answer" && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "green", bold: true }, "\u2705 Final Answer:", " ")), /* @__PURE__ */ React4.createElement(Box4, { paddingLeft: 2 }, /* @__PURE__ */ React4.createElement(Text4, null, step.content)), step.metadata?.tokenUsage && /* @__PURE__ */ React4.createElement(Box4, { paddingLeft: 2 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "(", step.metadata.tokenUsage.total, " tokens)"))))), !steps.some((s) => s.type === "final_answer") && /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "yellow" }, /* @__PURE__ */ React4.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React4.createElement(Text4, null, " ", steps.length === 0 ? "Thinking..." : "Processing...")));
681
+ var ThoughtStream = React6.memo(function ThoughtStream2({ steps }) {
682
+ return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column", gap: 1 }, steps.map((step, index) => /* @__PURE__ */ React6.createElement(Box5, { key: index, flexDirection: "column" }, step.type === "thought" && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, "\u{1F914} Thought: "), /* @__PURE__ */ React6.createElement(Text6, null, step.content)), step.type === "action" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "\u26A1 Action: "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, step.metadata?.toolName || "unknown")), step.metadata?.toolInput && /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, JSON.stringify(step.metadata.toolInput, null, 2)))), step.type === "observation" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, "\u{1F4CA} Observation: ")), /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, null, step.content))), step.type === "final_answer" && /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green", bold: true }, "\u2705 Final Answer:", " ")), /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, null, step.content)), step.metadata?.tokenUsage && /* @__PURE__ */ React6.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "(", step.metadata.tokenUsage.total, " tokens)"))))), !steps.some((s) => s.type === "final_answer") && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " ", steps.length === 0 ? "Thinking..." : "Processing...")));
302
683
  });
303
684
 
304
685
  // src/store/index.ts
@@ -307,6 +688,12 @@ var useCliStore = create((set) => ({
307
688
  // Session state
308
689
  session: null,
309
690
  setSession: (session) => set({ session }),
691
+ clearMessages: () => set((state) => ({
692
+ session: state.session ? {
693
+ ...state.session,
694
+ messages: []
695
+ } : null
696
+ })),
310
697
  // UI state
311
698
  isThinking: false,
312
699
  setIsThinking: (thinking) => set({ isThinking: thinking }),
@@ -324,32 +711,32 @@ var useCliStore = create((set) => ({
324
711
  }));
325
712
 
326
713
  // src/components/AgentThinking.tsx
327
- var AgentThinking = React5.memo(function AgentThinking2() {
714
+ var AgentThinking = React7.memo(function AgentThinking2() {
328
715
  const isThinking = useCliStore((state) => state.isThinking);
329
716
  const agentSteps = useCliStore((state) => state.agentSteps);
330
717
  if (!isThinking) {
331
718
  return null;
332
719
  }
333
- return /* @__PURE__ */ React5.createElement(Box5, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React5.createElement(ThoughtStream, { steps: agentSteps }));
720
+ return /* @__PURE__ */ React7.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
334
721
  });
335
722
 
336
723
  // src/components/PermissionPrompt.tsx
337
- import React6 from "react";
338
- import { Box as Box6, Text as Text5 } from "ink";
724
+ import React8 from "react";
725
+ import { Box as Box7, Text as Text7 } from "ink";
339
726
  import SelectInput from "ink-select-input";
340
727
  function renderDiffPreview(preview) {
341
728
  const lines = preview.split("\n");
342
729
  return lines.map((line, index) => {
343
730
  if (line.startsWith("+")) {
344
- return /* @__PURE__ */ React6.createElement(Text5, { key: index, color: "green" }, line);
731
+ return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "green" }, line);
345
732
  }
346
733
  if (line.startsWith("-")) {
347
- return /* @__PURE__ */ React6.createElement(Text5, { key: index, color: "red" }, line);
734
+ return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "red" }, line);
348
735
  }
349
736
  if (line.startsWith("@@")) {
350
- return /* @__PURE__ */ React6.createElement(Text5, { key: index, color: "cyan" }, line);
737
+ return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "cyan" }, line);
351
738
  }
352
- return /* @__PURE__ */ React6.createElement(Text5, { key: index, dimColor: true }, line);
739
+ return /* @__PURE__ */ React8.createElement(Text7, { key: index, dimColor: true }, line);
353
740
  });
354
741
  }
355
742
  function PermissionPrompt({
@@ -369,7 +756,121 @@ function PermissionPrompt({
369
756
  { label: "\u2717 Deny", value: "deny" }
370
757
  ];
371
758
  const argsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
372
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text5, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, "Tool: "), /* @__PURE__ */ React6.createElement(Text5, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, "Action: "), /* @__PURE__ */ React6.createElement(Text5, null, toolDescription)), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text5, { bold: true }, "Arguments:"), /* @__PURE__ */ React6.createElement(Box6, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text5, { bold: true }, "Preview:"), /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text5, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
759
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Tool: "), /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Action: "), /* @__PURE__ */ React8.createElement(Text7, null, toolDescription)), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Arguments:"), /* @__PURE__ */ React8.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Preview:"), /* @__PURE__ */ React8.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
760
+ }
761
+
762
+ // src/utils/processFileReferences.ts
763
+ import * as fs2 from "fs";
764
+ import * as path2 from "path";
765
+
766
+ // src/utils/constants.ts
767
+ var NAME_SUFFIXES = ["jr", "sr", "ii", "iii", "iv", "v", "phd", "md", "esq"];
768
+ function isNameSuffix(value) {
769
+ return NAME_SUFFIXES.includes(value);
770
+ }
771
+
772
+ // src/utils/processFileReferences.ts
773
+ var FILE_REFERENCE_REGEX = /(?:^|\s)@([^\s@]+)/g;
774
+ function looksLikeFilePath(ref) {
775
+ if (ref.includes("/") || ref.includes(path2.sep)) {
776
+ return true;
777
+ }
778
+ const extensionMatch = /\.(\w+)$/.exec(ref);
779
+ if (extensionMatch) {
780
+ const ext = extensionMatch[1].toLowerCase();
781
+ if (isNameSuffix(ext)) {
782
+ return false;
783
+ }
784
+ if (ext.length > 10) {
785
+ return false;
786
+ }
787
+ return true;
788
+ }
789
+ return false;
790
+ }
791
+ function extractFileReferences(message) {
792
+ const references = [];
793
+ const regex = /(?:^|\s)@([^\s@]+)/g;
794
+ let match;
795
+ while ((match = regex.exec(message)) !== null) {
796
+ const ref = match[1];
797
+ if (looksLikeFilePath(ref)) {
798
+ references.push(ref);
799
+ }
800
+ }
801
+ return references;
802
+ }
803
+ function readFileContents(filePath) {
804
+ const cwd = process.cwd();
805
+ const absolutePath = path2.resolve(cwd, filePath);
806
+ if (!isPathWithinCwd(filePath)) {
807
+ return { error: `Security: Path "${filePath}" is outside the current working directory` };
808
+ }
809
+ if (!fs2.existsSync(absolutePath)) {
810
+ return { error: `File not found: "${filePath}"` };
811
+ }
812
+ const stats = fs2.statSync(absolutePath);
813
+ if (stats.isDirectory()) {
814
+ try {
815
+ const entries = fs2.readdirSync(absolutePath);
816
+ const fileCount = entries.length;
817
+ return {
818
+ content: `(Directory with ${fileCount} items. Use file tools to explore if needed.)`,
819
+ size: 0
820
+ };
821
+ } catch (err) {
822
+ return { error: `Cannot read directory "${filePath}": ${err instanceof Error ? err.message : "Unknown error"}` };
823
+ }
824
+ }
825
+ if (stats.size > MAX_FILE_SIZE) {
826
+ return {
827
+ error: `File too large: "${filePath}" is ${formatFileSize(stats.size)} (max ${formatFileSize(MAX_FILE_SIZE)})`
828
+ };
829
+ }
830
+ if (isBinaryFile(filePath)) {
831
+ return { error: `Binary file: "${filePath}" cannot be included as text content` };
832
+ }
833
+ try {
834
+ const content = fs2.readFileSync(absolutePath, "utf-8");
835
+ return { content, size: stats.size };
836
+ } catch (err) {
837
+ return { error: `Cannot read file "${filePath}": ${err instanceof Error ? err.message : "Unknown error"}` };
838
+ }
839
+ }
840
+ function formatFileBlock(filePath, content, size, isDirectory) {
841
+ if (isDirectory) {
842
+ return `
843
+ --- Directory Reference: ${filePath} ---
844
+ ${content}
845
+ --- End of ${filePath} ---`;
846
+ }
847
+ return `
848
+ --- Referenced File: ${filePath} (${formatFileSize(size)}) ---
849
+ ${content}
850
+ --- End of ${filePath} ---`;
851
+ }
852
+ async function processFileReferences(message) {
853
+ const references = extractFileReferences(message);
854
+ const errors = [];
855
+ const fileBlocks = [];
856
+ for (const ref of references) {
857
+ const result = readFileContents(ref);
858
+ if ("error" in result) {
859
+ errors.push(result.error);
860
+ continue;
861
+ }
862
+ const isDirectory = result.size === 0 && result.content.startsWith("(Directory");
863
+ fileBlocks.push(formatFileBlock(ref, result.content, result.size, isDirectory));
864
+ }
865
+ if (fileBlocks.length === 0) {
866
+ return { content: message, errors };
867
+ }
868
+ const processedContent = message + "\n" + fileBlocks.join("\n");
869
+ return { content: processedContent, errors };
870
+ }
871
+ function hasFileReferences(message) {
872
+ FILE_REFERENCE_REGEX.lastIndex = 0;
873
+ return FILE_REFERENCE_REGEX.test(message);
373
874
  }
374
875
 
375
876
  // src/components/App.tsx
@@ -382,7 +883,7 @@ function App({ onMessage, onCommand, onPermissionResponse, commandHistory = [] }
382
883
  const permissionPrompt = useCliStore((state) => state.permissionPrompt);
383
884
  const exitRequested = useCliStore((state) => state.exitRequested);
384
885
  const setIsThinking = useCliStore((state) => state.setIsThinking);
385
- const handleSubmit = React7.useCallback(
886
+ const handleSubmit = React9.useCallback(
386
887
  async (input) => {
387
888
  const trimmed = input.trim();
388
889
  if (!trimmed) return;
@@ -393,14 +894,25 @@ function App({ onMessage, onCommand, onPermissionResponse, commandHistory = [] }
393
894
  }
394
895
  setIsThinking(true);
395
896
  try {
396
- await onMessage(trimmed);
897
+ let messageToSend = trimmed;
898
+ if (hasFileReferences(trimmed)) {
899
+ const processed = await processFileReferences(trimmed);
900
+ messageToSend = processed.content;
901
+ if (processed.errors.length > 0) {
902
+ const errorBlock = processed.errors.map((e) => `[Warning: ${e}]`).join("\n");
903
+ messageToSend = `${messageToSend}
904
+
905
+ ${errorBlock}`;
906
+ }
907
+ }
908
+ await onMessage(messageToSend);
397
909
  } finally {
398
910
  setIsThinking(false);
399
911
  }
400
912
  },
401
913
  [onMessage, onCommand, setIsThinking]
402
914
  );
403
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), permissionPrompt ? /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React7.createElement(
915
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), permissionPrompt ? /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
404
916
  PermissionPrompt,
405
917
  {
406
918
  toolName: permissionPrompt.toolName,
@@ -409,39 +921,39 @@ function App({ onMessage, onCommand, onPermissionResponse, commandHistory = [] }
409
921
  canBeTrusted: permissionPrompt.canBeTrusted,
410
922
  onResponse: onPermissionResponse
411
923
  }
412
- )) : /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React7.createElement(Box7, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React7.createElement(MessageItem, { message }))), /* @__PURE__ */ React7.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React7.createElement(Box7, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Text6, { color: "yellow", bold: true }, "\u26A0\uFE0F Press Ctrl+C again to exit")), /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React7.createElement(InputPrompt, { onSubmit: handleSubmit, disabled: isThinking, history: commandHistory }))));
924
+ )) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React9.createElement(Box8, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(MessageItem, { message }))), /* @__PURE__ */ React9.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, "\u26A0\uFE0F Press Ctrl+C again to exit")), /* @__PURE__ */ React9.createElement(Box8, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(InputPrompt, { onSubmit: handleSubmit, disabled: isThinking, history: commandHistory }))));
413
925
  }
414
926
  function MessageItem({ message }) {
415
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React7.createElement(Text6, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React7.createElement(Text6, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
927
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React9.createElement(Box8, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React9.createElement(Text8, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React9.createElement(Box8, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
416
928
  if (step.type === "thought") {
417
- return /* @__PURE__ */ React7.createElement(Box7, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
929
+ return /* @__PURE__ */ React9.createElement(Box8, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
418
930
  }
419
931
  if (step.type === "action") {
420
932
  const toolName = step.metadata?.toolName || "unknown";
421
933
  const toolInput = step.metadata?.toolInput;
422
934
  const observationStep = message.metadata.steps[idx + 1];
423
935
  const result = observationStep?.type === "observation" ? observationStep.content : null;
424
- return /* @__PURE__ */ React7.createElement(Box7, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
936
+ return /* @__PURE__ */ React9.createElement(Box8, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
425
937
  }
426
938
  return null;
427
- }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2 }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
939
+ }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
428
940
  }
429
941
 
430
942
  // src/components/MessageList.tsx
431
- import React8 from "react";
432
- import { Box as Box8, Text as Text7 } from "ink";
943
+ import React10 from "react";
944
+ import { Box as Box9, Text as Text9 } from "ink";
433
945
  function stripThinkingTags(content) {
434
946
  return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
435
947
  }
436
- var MessageList = React8.memo(
948
+ var MessageList = React10.memo(
437
949
  function MessageList2({ messages }) {
438
950
  if (messages.length === 0) {
439
- return /* @__PURE__ */ React8.createElement(Box8, { paddingY: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "No messages yet. Type a message to start!"));
951
+ return /* @__PURE__ */ React10.createElement(Box9, { paddingY: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "No messages yet. Type a message to start!"));
440
952
  }
441
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React8.createElement(Box8, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React8.createElement(Box8, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React8.createElement(Text7, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React8.createElement(Box8, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " tools used,", " ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
953
+ return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React10.createElement(Text9, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " tools used,", " ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
442
954
  if (step.type === "thought") {
443
- return /* @__PURE__ */ React8.createElement(Box8, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React8.createElement(
444
- Text7,
955
+ return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(
956
+ Text9,
445
957
  {
446
958
  dimColor: true
447
959
  },
@@ -453,10 +965,10 @@ var MessageList = React8.memo(
453
965
  const toolInput = step.metadata?.toolInput;
454
966
  const observationStep = message.metadata.steps[idx + 1];
455
967
  const result = observationStep?.type === "observation" ? observationStep.content : null;
456
- return /* @__PURE__ */ React8.createElement(Box8, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
968
+ return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
457
969
  }
458
970
  return null;
459
- }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React8.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
971
+ }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
460
972
  },
461
973
  (prevProps, nextProps) => {
462
974
  if (prevProps.messages.length !== nextProps.messages.length) {
@@ -467,8 +979,8 @@ var MessageList = React8.memo(
467
979
  );
468
980
 
469
981
  // src/components/LoginFlow.tsx
470
- import React9, { useState as useState2, useEffect as useEffect2 } from "react";
471
- import { Box as Box9, Text as Text8 } from "ink";
982
+ import React11, { useState as useState3, useEffect as useEffect2 } from "react";
983
+ import { Box as Box10, Text as Text10 } from "ink";
472
984
  import Spinner2 from "ink-spinner";
473
985
  import jwt from "jsonwebtoken";
474
986
  import open from "open";
@@ -578,16 +1090,16 @@ var OAuthClient = class {
578
1090
  * Sleep for specified milliseconds
579
1091
  */
580
1092
  sleep(ms) {
581
- return new Promise((resolve) => setTimeout(resolve, ms));
1093
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
582
1094
  }
583
1095
  };
584
1096
 
585
1097
  // src/components/LoginFlow.tsx
586
1098
  function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
587
- const [status, setStatus] = useState2("initiating");
588
- const [deviceFlow, setDeviceFlow] = useState2(null);
589
- const [statusMessage, setStatusMessage] = useState2("Initiating device authorization...");
590
- const [error, setError] = useState2(null);
1099
+ const [status, setStatus] = useState3("initiating");
1100
+ const [deviceFlow, setDeviceFlow] = useState3(null);
1101
+ const [statusMessage, setStatusMessage] = useState3("Initiating device authorization...");
1102
+ const [error, setError] = useState3(null);
591
1103
  useEffect2(() => {
592
1104
  const runLoginFlow = async () => {
593
1105
  const oauth = new OAuthClient(apiUrl);
@@ -631,31 +1143,31 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
631
1143
  }
632
1144
  }, [deviceFlow, status]);
633
1145
  if (status === "initiating") {
634
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, /* @__PURE__ */ React9.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
1146
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan" }, /* @__PURE__ */ React11.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
635
1147
  }
636
1148
  if (status === "error") {
637
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, error)));
1149
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { color: "red" }, error)));
638
1150
  }
639
1151
  if (status === "success") {
640
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "You can now use B4M CLI with your account.")));
1152
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "You can now use B4M CLI with your account.")));
641
1153
  }
642
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "And enter this code when prompted:")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, /* @__PURE__ */ React9.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
1154
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, null, "And enter this code when prompted:")), /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan" }, /* @__PURE__ */ React11.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
643
1155
  }
644
1156
 
645
1157
  // src/storage/SessionStore.ts
646
- import { promises as fs } from "fs";
647
- import path from "path";
1158
+ import { promises as fs3 } from "fs";
1159
+ import path3 from "path";
648
1160
  import { homedir } from "os";
649
1161
  var SessionStore = class {
650
1162
  constructor(basePath) {
651
- this.basePath = basePath || path.join(homedir(), ".bike4mind", "sessions");
1163
+ this.basePath = basePath || path3.join(homedir(), ".bike4mind", "sessions");
652
1164
  }
653
1165
  /**
654
1166
  * Initialize storage directory
655
1167
  */
656
1168
  async init() {
657
1169
  try {
658
- await fs.mkdir(this.basePath, { recursive: true });
1170
+ await fs3.mkdir(this.basePath, { recursive: true });
659
1171
  } catch (error) {
660
1172
  console.error("Failed to initialize session storage:", error);
661
1173
  throw error;
@@ -666,9 +1178,9 @@ var SessionStore = class {
666
1178
  */
667
1179
  async save(session) {
668
1180
  await this.init();
669
- const filePath = path.join(this.basePath, `${session.id}.json`);
1181
+ const filePath = path3.join(this.basePath, `${session.id}.json`);
670
1182
  try {
671
- await fs.writeFile(filePath, JSON.stringify(session, null, 2), "utf-8");
1183
+ await fs3.writeFile(filePath, JSON.stringify(session, null, 2), "utf-8");
672
1184
  } catch (error) {
673
1185
  console.error("Failed to save session:", error);
674
1186
  throw error;
@@ -678,9 +1190,9 @@ var SessionStore = class {
678
1190
  * Load a session from disk by ID
679
1191
  */
680
1192
  async load(id) {
681
- const filePath = path.join(this.basePath, `${id}.json`);
1193
+ const filePath = path3.join(this.basePath, `${id}.json`);
682
1194
  try {
683
- const data = await fs.readFile(filePath, "utf-8");
1195
+ const data = await fs3.readFile(filePath, "utf-8");
684
1196
  return JSON.parse(data);
685
1197
  } catch (error) {
686
1198
  if (error.code === "ENOENT") {
@@ -704,12 +1216,12 @@ var SessionStore = class {
704
1216
  async list() {
705
1217
  await this.init();
706
1218
  try {
707
- const files = await fs.readdir(this.basePath);
1219
+ const files = await fs3.readdir(this.basePath);
708
1220
  const jsonFiles = files.filter((f) => f.endsWith(".json"));
709
1221
  const sessions = await Promise.all(
710
1222
  jsonFiles.map(async (file) => {
711
- const filePath = path.join(this.basePath, file);
712
- const data = await fs.readFile(filePath, "utf-8");
1223
+ const filePath = path3.join(this.basePath, file);
1224
+ const data = await fs3.readFile(filePath, "utf-8");
713
1225
  return JSON.parse(data);
714
1226
  })
715
1227
  );
@@ -723,9 +1235,9 @@ var SessionStore = class {
723
1235
  * Delete a session
724
1236
  */
725
1237
  async delete(id) {
726
- const filePath = path.join(this.basePath, `${id}.json`);
1238
+ const filePath = path3.join(this.basePath, `${id}.json`);
727
1239
  try {
728
- await fs.unlink(filePath);
1240
+ await fs3.unlink(filePath);
729
1241
  return true;
730
1242
  } catch (error) {
731
1243
  if (error.code === "ENOENT") {
@@ -761,8 +1273,8 @@ var SessionStore = class {
761
1273
  };
762
1274
 
763
1275
  // src/storage/ConfigStore.ts
764
- import { promises as fs2 } from "fs";
765
- import path2 from "path";
1276
+ import { promises as fs4 } from "fs";
1277
+ import path4 from "path";
766
1278
  import { homedir as homedir2 } from "os";
767
1279
  import { v4 as uuidv4 } from "uuid";
768
1280
  import { z } from "zod";
@@ -836,15 +1348,15 @@ var DEFAULT_CONFIG = {
836
1348
  var ConfigStore = class {
837
1349
  constructor(configPath) {
838
1350
  this.config = null;
839
- this.configPath = configPath || path2.join(homedir2(), ".bike4mind", "config.json");
1351
+ this.configPath = configPath || path4.join(homedir2(), ".bike4mind", "config.json");
840
1352
  }
841
1353
  /**
842
1354
  * Initialize config directory
843
1355
  */
844
1356
  async init() {
845
- const dir = path2.dirname(this.configPath);
1357
+ const dir = path4.dirname(this.configPath);
846
1358
  try {
847
- await fs2.mkdir(dir, { recursive: true });
1359
+ await fs4.mkdir(dir, { recursive: true });
848
1360
  } catch (error) {
849
1361
  console.error("Failed to initialize config directory:", error);
850
1362
  throw error;
@@ -859,15 +1371,15 @@ var ConfigStore = class {
859
1371
  }
860
1372
  try {
861
1373
  try {
862
- const stats = await fs2.stat(this.configPath);
1374
+ const stats = await fs4.stat(this.configPath);
863
1375
  const mode = stats.mode & 511;
864
1376
  if (mode !== 384) {
865
1377
  console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
866
- await fs2.chmod(this.configPath, 384);
1378
+ await fs4.chmod(this.configPath, 384);
867
1379
  }
868
1380
  } catch (statError) {
869
1381
  }
870
- const data = await fs2.readFile(this.configPath, "utf-8");
1382
+ const data = await fs4.readFile(this.configPath, "utf-8");
871
1383
  const rawConfig = JSON.parse(data);
872
1384
  if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
873
1385
  const oldApiConfig = rawConfig.apiConfig;
@@ -940,8 +1452,8 @@ var ConfigStore = class {
940
1452
  throw new Error("No configuration to save");
941
1453
  }
942
1454
  try {
943
- await fs2.writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
944
- await fs2.chmod(this.configPath, 384);
1455
+ await fs4.writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
1456
+ await fs4.chmod(this.configPath, 384);
945
1457
  } catch (error) {
946
1458
  console.error("Failed to save config:", error);
947
1459
  throw error;
@@ -1088,22 +1600,22 @@ var ConfigStore = class {
1088
1600
  };
1089
1601
 
1090
1602
  // src/storage/CommandHistoryStore.ts
1091
- import { promises as fs3 } from "fs";
1092
- import path3 from "path";
1603
+ import { promises as fs5 } from "fs";
1604
+ import path5 from "path";
1093
1605
  import { homedir as homedir3 } from "os";
1094
1606
  var MAX_HISTORY_ENTRIES = 1e3;
1095
1607
  var CommandHistoryStore = class {
1096
1608
  constructor(historyPath) {
1097
1609
  this.history = null;
1098
- this.historyPath = historyPath || path3.join(homedir3(), ".bike4mind", "history.jsonl");
1610
+ this.historyPath = historyPath || path5.join(homedir3(), ".bike4mind", "history.jsonl");
1099
1611
  }
1100
1612
  /**
1101
1613
  * Initialize history directory
1102
1614
  */
1103
1615
  async init() {
1104
- const dir = path3.dirname(this.historyPath);
1616
+ const dir = path5.dirname(this.historyPath);
1105
1617
  try {
1106
- await fs3.mkdir(dir, { recursive: true });
1618
+ await fs5.mkdir(dir, { recursive: true });
1107
1619
  } catch (error) {
1108
1620
  console.error("Failed to initialize history directory:", error);
1109
1621
  throw error;
@@ -1118,7 +1630,7 @@ var CommandHistoryStore = class {
1118
1630
  return this.history;
1119
1631
  }
1120
1632
  try {
1121
- const data = await fs3.readFile(this.historyPath, "utf-8");
1633
+ const data = await fs5.readFile(this.historyPath, "utf-8");
1122
1634
  const lines = data.trim().split("\n").filter((line) => line.length > 0);
1123
1635
  const entries = lines.map((line) => {
1124
1636
  try {
@@ -1155,7 +1667,7 @@ var CommandHistoryStore = class {
1155
1667
  };
1156
1668
  await this.init();
1157
1669
  const line = JSON.stringify(entry) + "\n";
1158
- await fs3.appendFile(this.historyPath, line, "utf-8");
1670
+ await fs5.appendFile(this.historyPath, line, "utf-8");
1159
1671
  this.history = [command, ...this.history || []];
1160
1672
  if (this.history.length > MAX_HISTORY_ENTRIES) {
1161
1673
  await this.trim();
@@ -1178,7 +1690,7 @@ var CommandHistoryStore = class {
1178
1690
  };
1179
1691
  return JSON.stringify(entry);
1180
1692
  }).join("\n");
1181
- await fs3.writeFile(this.historyPath, lines + "\n", "utf-8");
1693
+ await fs5.writeFile(this.historyPath, lines + "\n", "utf-8");
1182
1694
  this.history = trimmedCommands;
1183
1695
  }
1184
1696
  /**
@@ -1186,7 +1698,7 @@ var CommandHistoryStore = class {
1186
1698
  */
1187
1699
  async clear() {
1188
1700
  try {
1189
- await fs3.unlink(this.historyPath);
1701
+ await fs5.unlink(this.historyPath);
1190
1702
  } catch (error) {
1191
1703
  if (error.code !== "ENOENT") {
1192
1704
  throw error;
@@ -1960,7 +2472,7 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
1960
2472
  import OpenAI2 from "openai";
1961
2473
 
1962
2474
  // ../../b4m-core/packages/services/dist/src/importHistoryService/index.js
1963
- import fs4, { unlinkSync } from "fs";
2475
+ import fs6, { unlinkSync } from "fs";
1964
2476
  import yauzl from "yauzl";
1965
2477
  import axios3 from "axios";
1966
2478
 
@@ -3646,8 +4158,8 @@ async function processAndStoreImages(images, context) {
3646
4158
  const buffer = await downloadImage(image);
3647
4159
  const fileType = await fileTypeFromBuffer2(buffer);
3648
4160
  const filename = `${uuidv45()}.${fileType?.ext}`;
3649
- const path11 = await context.imageGenerateStorage.upload(buffer, filename, {});
3650
- return path11;
4161
+ const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
4162
+ return path13;
3651
4163
  }));
3652
4164
  }
3653
4165
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -4831,8 +5343,8 @@ async function processAndStoreImage(imageUrl, context) {
4831
5343
  const buffer = await downloadImage2(imageUrl);
4832
5344
  const fileType = await fileTypeFromBuffer3(buffer);
4833
5345
  const filename = `${uuidv46()}.${fileType?.ext}`;
4834
- const path11 = await context.imageGenerateStorage.upload(buffer, filename, {});
4835
- return path11;
5346
+ const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
5347
+ return path13;
4836
5348
  }
4837
5349
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
4838
5350
  await context.onFinish?.("edit_image", storedImagePath);
@@ -6669,33 +7181,33 @@ var planetVisibilityTool = {
6669
7181
  };
6670
7182
 
6671
7183
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
6672
- import { promises as fs5 } from "fs";
6673
- import { existsSync, statSync } from "fs";
6674
- import path4 from "path";
6675
- var MAX_FILE_SIZE = 10 * 1024 * 1024;
7184
+ import { promises as fs7 } from "fs";
7185
+ import { existsSync as existsSync2, statSync as statSync3 } from "fs";
7186
+ import path6 from "path";
7187
+ var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
6676
7188
  async function readFileContent(params) {
6677
7189
  const { path: filePath, encoding = "utf-8", maxLines } = params;
6678
- const normalizedPath = path4.normalize(filePath);
6679
- const resolvedPath = path4.resolve(process.cwd(), normalizedPath);
6680
- const cwd = path4.resolve(process.cwd());
7190
+ const normalizedPath = path6.normalize(filePath);
7191
+ const resolvedPath = path6.resolve(process.cwd(), normalizedPath);
7192
+ const cwd = path6.resolve(process.cwd());
6681
7193
  if (!resolvedPath.startsWith(cwd)) {
6682
7194
  throw new Error(`Access denied: Cannot read files outside of current working directory`);
6683
7195
  }
6684
- if (!existsSync(resolvedPath)) {
7196
+ if (!existsSync2(resolvedPath)) {
6685
7197
  throw new Error(`File not found: ${filePath}`);
6686
7198
  }
6687
- const stats = statSync(resolvedPath);
7199
+ const stats = statSync3(resolvedPath);
6688
7200
  if (stats.isDirectory()) {
6689
7201
  throw new Error(`Path is a directory, not a file: ${filePath}`);
6690
7202
  }
6691
- if (stats.size > MAX_FILE_SIZE) {
6692
- throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE / 1024 / 1024}MB)`);
7203
+ if (stats.size > MAX_FILE_SIZE2) {
7204
+ throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE2 / 1024 / 1024}MB)`);
6693
7205
  }
6694
7206
  const isBinary = await checkIfBinary(resolvedPath);
6695
7207
  if (isBinary && encoding === "utf-8") {
6696
7208
  throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
6697
7209
  }
6698
- const content = await fs5.readFile(resolvedPath, encoding);
7210
+ const content = await fs7.readFile(resolvedPath, encoding);
6699
7211
  if (maxLines && typeof content === "string") {
6700
7212
  const lines = content.split("\n");
6701
7213
  if (lines.length > maxLines) {
@@ -6710,7 +7222,7 @@ ${content}`;
6710
7222
  }
6711
7223
  async function checkIfBinary(filePath) {
6712
7224
  const buffer = Buffer.alloc(8192);
6713
- const fd = await fs5.open(filePath, "r");
7225
+ const fd = await fs7.open(filePath, "r");
6714
7226
  try {
6715
7227
  const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
6716
7228
  const chunk = buffer.slice(0, bytesRead);
@@ -6727,7 +7239,7 @@ var fileReadTool = {
6727
7239
  context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
6728
7240
  try {
6729
7241
  const content = await readFileContent(params);
6730
- const stats = statSync(path4.resolve(process.cwd(), path4.normalize(params.path)));
7242
+ const stats = statSync3(path6.resolve(process.cwd(), path6.normalize(params.path)));
6731
7243
  context.logger.info("\u2705 FileRead: Success", {
6732
7244
  path: params.path,
6733
7245
  size: stats.size,
@@ -6766,25 +7278,25 @@ var fileReadTool = {
6766
7278
  };
6767
7279
 
6768
7280
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
6769
- import { promises as fs6 } from "fs";
6770
- import { existsSync as existsSync2 } from "fs";
6771
- import path5 from "path";
7281
+ import { promises as fs8 } from "fs";
7282
+ import { existsSync as existsSync3 } from "fs";
7283
+ import path7 from "path";
6772
7284
  async function createFile(params) {
6773
7285
  const { path: filePath, content, createDirectories = true } = params;
6774
- const normalizedPath = path5.normalize(filePath);
6775
- const resolvedPath = path5.resolve(process.cwd(), normalizedPath);
6776
- const cwd = path5.resolve(process.cwd());
7286
+ const normalizedPath = path7.normalize(filePath);
7287
+ const resolvedPath = path7.resolve(process.cwd(), normalizedPath);
7288
+ const cwd = path7.resolve(process.cwd());
6777
7289
  if (!resolvedPath.startsWith(cwd)) {
6778
7290
  throw new Error(`Access denied: Cannot create files outside of current working directory`);
6779
7291
  }
6780
- const fileExists = existsSync2(resolvedPath);
7292
+ const fileExists = existsSync3(resolvedPath);
6781
7293
  const action = fileExists ? "overwritten" : "created";
6782
7294
  if (createDirectories) {
6783
- const dir = path5.dirname(resolvedPath);
6784
- await fs6.mkdir(dir, { recursive: true });
7295
+ const dir = path7.dirname(resolvedPath);
7296
+ await fs8.mkdir(dir, { recursive: true });
6785
7297
  }
6786
- await fs6.writeFile(resolvedPath, content, "utf-8");
6787
- const stats = await fs6.stat(resolvedPath);
7298
+ await fs8.writeFile(resolvedPath, content, "utf-8");
7299
+ const stats = await fs8.stat(resolvedPath);
6788
7300
  const lines = content.split("\n").length;
6789
7301
  return `File ${action} successfully: ${filePath}
6790
7302
  Size: ${stats.size} bytes
@@ -6795,7 +7307,7 @@ var createFileTool = {
6795
7307
  implementation: (context) => ({
6796
7308
  toolFn: async (value) => {
6797
7309
  const params = value;
6798
- const fileExists = existsSync2(path5.resolve(process.cwd(), path5.normalize(params.path)));
7310
+ const fileExists = existsSync3(path7.resolve(process.cwd(), path7.normalize(params.path)));
6799
7311
  context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
6800
7312
  path: params.path,
6801
7313
  size: params.content.length
@@ -6836,8 +7348,8 @@ var createFileTool = {
6836
7348
 
6837
7349
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
6838
7350
  import { readdir } from "fs/promises";
6839
- import { statSync as statSync2 } from "fs";
6840
- import path6 from "path";
7351
+ import { statSync as statSync4 } from "fs";
7352
+ import path8 from "path";
6841
7353
  function matchGlob(filePath, pattern) {
6842
7354
  let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
6843
7355
  regexPattern = regexPattern.replace(/\{\{GLOBSTAR\}\}\//g, "(.*/)?").replace(/\/\{\{GLOBSTAR\}\}/g, "(/.*)?").replace(/\{\{GLOBSTAR\}\}/g, ".*");
@@ -6855,8 +7367,8 @@ async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
6855
7367
  if (!includeHidden && entry.name.startsWith(".")) {
6856
7368
  continue;
6857
7369
  }
6858
- const relativePath = basePath ? path6.join(basePath, entry.name) : entry.name;
6859
- const fullPath = path6.join(dir, entry.name);
7370
+ const relativePath = basePath ? path8.join(basePath, entry.name) : entry.name;
7371
+ const fullPath = path8.join(dir, entry.name);
6860
7372
  if (matchGlob(relativePath, pattern)) {
6861
7373
  matches.push(relativePath);
6862
7374
  }
@@ -6872,7 +7384,7 @@ async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
6872
7384
  async function listFiles(params) {
6873
7385
  const { pattern, cwd: relativeCwd, includeHidden = false, maxResults = 1e3 } = params;
6874
7386
  const baseCwd = process.cwd();
6875
- const targetCwd = relativeCwd ? path6.resolve(baseCwd, path6.normalize(relativeCwd)) : baseCwd;
7387
+ const targetCwd = relativeCwd ? path8.resolve(baseCwd, path8.normalize(relativeCwd)) : baseCwd;
6876
7388
  if (!targetCwd.startsWith(baseCwd)) {
6877
7389
  throw new Error(`Access denied: Cannot list files outside of current working directory`);
6878
7390
  }
@@ -6883,8 +7395,8 @@ async function listFiles(params) {
6883
7395
  const filesInfo = [];
6884
7396
  for (const match of matches) {
6885
7397
  try {
6886
- const fullPath = path6.join(targetCwd, match);
6887
- const stats = statSync2(fullPath);
7398
+ const fullPath = path8.join(targetCwd, match);
7399
+ const stats = statSync4(fullPath);
6888
7400
  filesInfo.push({
6889
7401
  path: match,
6890
7402
  size: stats.size,
@@ -6965,9 +7477,9 @@ var globFilesTool = {
6965
7477
 
6966
7478
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
6967
7479
  import { readdir as readdir2 } from "fs/promises";
6968
- import { readFileSync, statSync as statSync3 } from "fs";
6969
- import path7 from "path";
6970
- var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
7480
+ import { readFileSync as readFileSync2, statSync as statSync5 } from "fs";
7481
+ import path9 from "path";
7482
+ var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
6971
7483
  function matchGlob2(filePath, pattern) {
6972
7484
  const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
6973
7485
  const regex = new RegExp(`^${regexPattern}$`);
@@ -6984,8 +7496,8 @@ async function walkDir2(dir, pattern, maxResults, basePath = "") {
6984
7496
  if (entry.name.startsWith(".")) {
6985
7497
  continue;
6986
7498
  }
6987
- const relativePath = basePath ? path7.join(basePath, entry.name) : entry.name;
6988
- const fullPath = path7.join(dir, entry.name);
7499
+ const relativePath = basePath ? path9.join(basePath, entry.name) : entry.name;
7500
+ const fullPath = path9.join(dir, entry.name);
6989
7501
  if (matchGlob2(relativePath, pattern)) {
6990
7502
  matches.push(relativePath);
6991
7503
  }
@@ -6998,10 +7510,10 @@ async function walkDir2(dir, pattern, maxResults, basePath = "") {
6998
7510
  }
6999
7511
  return matches;
7000
7512
  }
7001
- async function searchFiles(params) {
7513
+ async function searchFiles2(params) {
7002
7514
  const { pattern, filePattern = "**/*", cwd: relativeCwd, caseSensitive = false, maxResults = 100, contextLines = 0 } = params;
7003
7515
  const baseCwd = process.cwd();
7004
- const targetCwd = relativeCwd ? path7.resolve(baseCwd, path7.normalize(relativeCwd)) : baseCwd;
7516
+ const targetCwd = relativeCwd ? path9.resolve(baseCwd, path9.normalize(relativeCwd)) : baseCwd;
7005
7517
  if (!targetCwd.startsWith(baseCwd)) {
7006
7518
  throw new Error(`Access denied: Cannot search files outside of current working directory`);
7007
7519
  }
@@ -7015,13 +7527,13 @@ async function searchFiles(params) {
7015
7527
  const filesToSearch = await walkDir2(targetCwd, filePattern, 1e4);
7016
7528
  const filteredFiles = [];
7017
7529
  for (const match of filesToSearch) {
7018
- const fullPath = path7.join(targetCwd, match);
7530
+ const fullPath = path9.join(targetCwd, match);
7019
7531
  try {
7020
- const stats = statSync3(fullPath);
7021
- if (stats.isDirectory() || stats.size > MAX_FILE_SIZE2) {
7532
+ const stats = statSync5(fullPath);
7533
+ if (stats.isDirectory() || stats.size > MAX_FILE_SIZE3) {
7022
7534
  continue;
7023
7535
  }
7024
- if (isBinaryFile(fullPath)) {
7536
+ if (isBinaryFile2(fullPath)) {
7025
7537
  continue;
7026
7538
  }
7027
7539
  filteredFiles.push(match);
@@ -7039,9 +7551,9 @@ async function searchFiles(params) {
7039
7551
  break;
7040
7552
  }
7041
7553
  filesSearched++;
7042
- const fullPath = path7.join(targetCwd, file);
7554
+ const fullPath = path9.join(targetCwd, file);
7043
7555
  try {
7044
- const content = readFileSync(fullPath, "utf-8");
7556
+ const content = readFileSync2(fullPath, "utf-8");
7045
7557
  const lines = content.split("\n");
7046
7558
  for (let i = 0; i < lines.length; i++) {
7047
7559
  if (matches.length >= maxResults) {
@@ -7105,7 +7617,7 @@ ${match.context.after.map((l) => ` ${l}`).join("\n")}`;
7105
7617
  }).join("\n");
7106
7618
  return `${summary}${patternInfo}${cwdInfo}${matchesList}`;
7107
7619
  }
7108
- function isBinaryFile(filePath) {
7620
+ function isBinaryFile2(filePath) {
7109
7621
  try {
7110
7622
  const buffer = Buffer.alloc(8192);
7111
7623
  const fd = __require("fs").openSync(filePath, "r");
@@ -7128,7 +7640,7 @@ var grepSearchTool = {
7128
7640
  cwd: params.cwd || "."
7129
7641
  });
7130
7642
  try {
7131
- const result = await searchFiles(params);
7643
+ const result = await searchFiles2(params);
7132
7644
  context.logger.info("\u2705 GrepSearch: Success");
7133
7645
  return result;
7134
7646
  } catch (error) {
@@ -7174,31 +7686,31 @@ var grepSearchTool = {
7174
7686
  };
7175
7687
 
7176
7688
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
7177
- import { promises as fs7 } from "fs";
7178
- import { existsSync as existsSync3, statSync as statSync4 } from "fs";
7179
- import path8 from "path";
7689
+ import { promises as fs9 } from "fs";
7690
+ import { existsSync as existsSync4, statSync as statSync6 } from "fs";
7691
+ import path10 from "path";
7180
7692
  async function deleteFile(params) {
7181
7693
  const { path: filePath, recursive = false } = params;
7182
- const normalizedPath = path8.normalize(filePath);
7183
- const resolvedPath = path8.resolve(process.cwd(), normalizedPath);
7184
- const cwd = path8.resolve(process.cwd());
7694
+ const normalizedPath = path10.normalize(filePath);
7695
+ const resolvedPath = path10.resolve(process.cwd(), normalizedPath);
7696
+ const cwd = path10.resolve(process.cwd());
7185
7697
  if (!resolvedPath.startsWith(cwd)) {
7186
7698
  throw new Error(`Access denied: Cannot delete files outside of current working directory`);
7187
7699
  }
7188
- if (!existsSync3(resolvedPath)) {
7700
+ if (!existsSync4(resolvedPath)) {
7189
7701
  throw new Error(`File or directory not found: ${filePath}`);
7190
7702
  }
7191
- const stats = statSync4(resolvedPath);
7703
+ const stats = statSync6(resolvedPath);
7192
7704
  const isDirectory = stats.isDirectory();
7193
7705
  const size = stats.size;
7194
7706
  if (isDirectory && !recursive) {
7195
7707
  throw new Error(`Path is a directory: ${filePath}. Use recursive=true to delete directories and their contents.`);
7196
7708
  }
7197
7709
  if (isDirectory) {
7198
- await fs7.rm(resolvedPath, { recursive: true, force: true });
7710
+ await fs9.rm(resolvedPath, { recursive: true, force: true });
7199
7711
  return `Directory deleted successfully: ${filePath}`;
7200
7712
  } else {
7201
- await fs7.unlink(resolvedPath);
7713
+ await fs9.unlink(resolvedPath);
7202
7714
  return `File deleted successfully: ${filePath}
7203
7715
  Size: ${size} bytes`;
7204
7716
  }
@@ -7208,8 +7720,8 @@ var deleteFileTool = {
7208
7720
  implementation: (context) => ({
7209
7721
  toolFn: async (value) => {
7210
7722
  const params = value;
7211
- const resolvedPath = path8.resolve(process.cwd(), path8.normalize(params.path));
7212
- const isDirectory = existsSync3(resolvedPath) && statSync4(resolvedPath).isDirectory();
7723
+ const resolvedPath = path10.resolve(process.cwd(), path10.normalize(params.path));
7724
+ const isDirectory = existsSync4(resolvedPath) && statSync6(resolvedPath).isDirectory();
7213
7725
  context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
7214
7726
  path: params.path,
7215
7727
  recursive: params.recursive
@@ -7499,10 +8011,10 @@ import jwt2 from "jsonwebtoken";
7499
8011
  // src/utils/diffPreview.ts
7500
8012
  import * as Diff from "diff";
7501
8013
  import { readFile } from "fs/promises";
7502
- import { existsSync as existsSync4 } from "fs";
8014
+ import { existsSync as existsSync5 } from "fs";
7503
8015
  async function generateFileDiffPreview(args) {
7504
8016
  try {
7505
- if (!existsSync4(args.path)) {
8017
+ if (!existsSync5(args.path)) {
7506
8018
  const lines2 = args.content.split("\n");
7507
8019
  const preview = lines2.slice(0, 20).join("\n");
7508
8020
  const hasMore = lines2.length > 20;
@@ -7532,10 +8044,10 @@ ${preview}${hasMore ? `
7532
8044
  }
7533
8045
  async function generateFileDeletePreview(args) {
7534
8046
  try {
7535
- if (!existsSync4(args.path)) {
8047
+ if (!existsSync5(args.path)) {
7536
8048
  return `[File does not exist: ${args.path}]`;
7537
8049
  }
7538
- const stats = await import("fs/promises").then((fs9) => fs9.stat(args.path));
8050
+ const stats = await import("fs/promises").then((fs11) => fs11.stat(args.path));
7539
8051
  return `[File will be deleted]
7540
8052
 
7541
8053
  Path: ${args.path}
@@ -7551,21 +8063,21 @@ var NoOpStorage = class extends BaseStorage {
7551
8063
  async upload(input, destination, options) {
7552
8064
  return `/tmp/${destination}`;
7553
8065
  }
7554
- async download(path11) {
8066
+ async download(path13) {
7555
8067
  throw new Error("Download not supported in CLI");
7556
8068
  }
7557
- async delete(path11) {
8069
+ async delete(path13) {
7558
8070
  }
7559
- async getSignedUrl(path11) {
7560
- return `/tmp/${path11}`;
8071
+ async getSignedUrl(path13) {
8072
+ return `/tmp/${path13}`;
7561
8073
  }
7562
- getPublicUrl(path11) {
7563
- return `/tmp/${path11}`;
8074
+ getPublicUrl(path13) {
8075
+ return `/tmp/${path13}`;
7564
8076
  }
7565
- async getPreview(path11) {
7566
- return `/tmp/${path11}`;
8077
+ async getPreview(path13) {
8078
+ return `/tmp/${path13}`;
7567
8079
  }
7568
- async getMetadata(path11) {
8080
+ async getMetadata(path13) {
7569
8081
  return { size: 0, contentType: "application/octet-stream" };
7570
8082
  }
7571
8083
  };
@@ -7891,8 +8403,8 @@ function getEnvironmentName(configApiConfig) {
7891
8403
  }
7892
8404
 
7893
8405
  // src/utils/Logger.ts
7894
- import fs8 from "fs/promises";
7895
- import path9 from "path";
8406
+ import fs10 from "fs/promises";
8407
+ import path11 from "path";
7896
8408
  import os from "os";
7897
8409
  var Logger2 = class _Logger {
7898
8410
  constructor() {
@@ -7915,9 +8427,9 @@ var Logger2 = class _Logger {
7915
8427
  */
7916
8428
  async initialize(sessionId) {
7917
8429
  this.sessionId = sessionId;
7918
- const debugDir = path9.join(os.homedir(), ".bike4mind", "debug");
7919
- await fs8.mkdir(debugDir, { recursive: true });
7920
- this.logFilePath = path9.join(debugDir, `${sessionId}.txt`);
8430
+ const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
8431
+ await fs10.mkdir(debugDir, { recursive: true });
8432
+ this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
7921
8433
  await this.writeToFile("INFO", "=== CLI SESSION START ===");
7922
8434
  }
7923
8435
  /**
@@ -7981,7 +8493,7 @@ var Logger2 = class _Logger {
7981
8493
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
7982
8494
  const logEntry = `[${timestamp}] [${level}] ${message}
7983
8495
  `;
7984
- await fs8.appendFile(this.logFilePath, logEntry, "utf-8");
8496
+ await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
7985
8497
  } catch (error) {
7986
8498
  console.error("File logging failed:", error);
7987
8499
  }
@@ -8127,15 +8639,15 @@ var Logger2 = class _Logger {
8127
8639
  async cleanupOldLogs() {
8128
8640
  if (!this.fileLoggingEnabled) return;
8129
8641
  try {
8130
- const debugDir = path9.join(os.homedir(), ".bike4mind", "debug");
8131
- const files = await fs8.readdir(debugDir);
8642
+ const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
8643
+ const files = await fs10.readdir(debugDir);
8132
8644
  const now = Date.now();
8133
8645
  const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
8134
8646
  for (const file of files) {
8135
- const filePath = path9.join(debugDir, file);
8136
- const stats = await fs8.stat(filePath);
8647
+ const filePath = path11.join(debugDir, file);
8648
+ const stats = await fs10.stat(filePath);
8137
8649
  if (stats.mtime.getTime() < thirtyDaysAgo) {
8138
- await fs8.unlink(filePath);
8650
+ await fs10.unlink(filePath);
8139
8651
  }
8140
8652
  }
8141
8653
  } catch (error) {
@@ -8148,8 +8660,8 @@ var logger = Logger2.getInstance();
8148
8660
  // ../../b4m-core/packages/mcp/dist/src/client.js
8149
8661
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
8150
8662
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
8151
- import path10 from "path";
8152
- import { existsSync as existsSync5, readdirSync } from "fs";
8663
+ import path12 from "path";
8664
+ import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
8153
8665
  var MCPClient = class {
8154
8666
  // Note: This class handles MCP server communication with repository filtering
8155
8667
  mcp;
@@ -8174,20 +8686,20 @@ var MCPClient = class {
8174
8686
  const root = process.env.INIT_CWD || process.cwd();
8175
8687
  const candidatePaths = [
8176
8688
  // When running from SST Lambda with node_modules structure (copyFiles)
8177
- path10.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
8689
+ path12.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
8178
8690
  // When running from SST Lambda deployed environment (/var/task)
8179
- path10.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8691
+ path12.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8180
8692
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
8181
- path10.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8693
+ path12.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8182
8694
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
8183
- path10.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8695
+ path12.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8184
8696
  // Original paths (backward compatibility)
8185
- path10.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8186
- path10.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
8697
+ path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8698
+ path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
8187
8699
  ];
8188
- const serverScriptPath = candidatePaths.find((p) => existsSync5(p));
8700
+ const serverScriptPath = candidatePaths.find((p) => existsSync6(p));
8189
8701
  if (!serverScriptPath) {
8190
- const getDirectories = (source) => readdirSync(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
8702
+ const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
8191
8703
  console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
8192
8704
  console.error(`[MCP] Available directories in ${root}:`, getDirectories(root));
8193
8705
  throw new Error(`MCP Server script path not found for ${this.serverName}. Root: ${root}, Available dirs: ${getDirectories(root).join(", ")}`);
@@ -8216,7 +8728,7 @@ var MCPClient = class {
8216
8728
  }
8217
8729
  };
8218
8730
  await this.mcp.connect(this.transport);
8219
- await new Promise((resolve) => setTimeout(resolve, 100));
8731
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
8220
8732
  const toolsResult = await this.mcp.listTools();
8221
8733
  this.tools = toolsResult.tools.map((tool) => {
8222
8734
  return {
@@ -8399,7 +8911,7 @@ var ServerLlmBackend = class {
8399
8911
  */
8400
8912
  async complete(model, messages, options, callback) {
8401
8913
  logger.debug(`[ServerLlmBackend] Starting complete() with model: ${model}`);
8402
- return new Promise(async (resolve, reject) => {
8914
+ return new Promise(async (resolve3, reject) => {
8403
8915
  try {
8404
8916
  logger.debug("[ServerLlmBackend] Making streaming request...");
8405
8917
  const response = await this.makeStreamingRequest(model, messages, options);
@@ -8429,15 +8941,15 @@ var ServerLlmBackend = class {
8429
8941
  reject(err);
8430
8942
  }).then(() => {
8431
8943
  logger.debug("[ServerLlmBackend] Callback completed, resolving");
8432
- resolve();
8944
+ resolve3();
8433
8945
  });
8434
8946
  } else if (cleanedText) {
8435
8947
  callback([cleanedText], lastUsageInfo).catch((err) => {
8436
8948
  logger.error("[ServerLlmBackend] Callback error:", err);
8437
8949
  reject(err);
8438
- }).then(() => resolve());
8950
+ }).then(() => resolve3());
8439
8951
  } else {
8440
- resolve();
8952
+ resolve3();
8441
8953
  }
8442
8954
  return;
8443
8955
  }
@@ -8497,7 +9009,7 @@ var ServerLlmBackend = class {
8497
9009
  response.data.on("end", () => {
8498
9010
  if (!receivedDone) {
8499
9011
  logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
8500
- resolve();
9012
+ resolve3();
8501
9013
  } else {
8502
9014
  logger.debug("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
8503
9015
  }
@@ -8761,10 +9273,13 @@ var ApiClient = class {
8761
9273
  }
8762
9274
  };
8763
9275
 
9276
+ // src/index.tsx
9277
+ import { isAxiosError as isAxiosError2 } from "axios";
9278
+
8764
9279
  // package.json
8765
9280
  var package_default = {
8766
9281
  name: "@bike4mind/cli",
8767
- version: "0.2.1-quest-file-uploads.17007+7890a35e3",
9282
+ version: "0.2.3-basic-cli-shortcuts.17058+9fe0019f0",
8768
9283
  type: "module",
8769
9284
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
8770
9285
  license: "UNLICENSED",
@@ -8775,6 +9290,9 @@ var package_default = {
8775
9290
  },
8776
9291
  homepage: "https://github.com/bike4mind/lumina5#readme",
8777
9292
  bugs: "https://github.com/bike4mind/lumina5/issues",
9293
+ publishConfig: {
9294
+ access: "public"
9295
+ },
8778
9296
  keywords: [
8779
9297
  "cli",
8780
9298
  "ai",
@@ -8795,6 +9313,8 @@ var package_default = {
8795
9313
  dev: "tsx src/index.tsx",
8796
9314
  build: "tsup",
8797
9315
  typecheck: "tsc --noEmit",
9316
+ test: "vitest run",
9317
+ "test:watch": "vitest",
8798
9318
  start: "node dist/index.js",
8799
9319
  prepublishOnly: "pnpm build"
8800
9320
  },
@@ -8861,10 +9381,10 @@ var package_default = {
8861
9381
  },
8862
9382
  devDependencies: {
8863
9383
  "@bike4mind/agents": "0.1.0",
8864
- "@bike4mind/common": "2.36.1-quest-file-uploads.17007+7890a35e3",
8865
- "@bike4mind/mcp": "1.19.4-quest-file-uploads.17007+7890a35e3",
8866
- "@bike4mind/services": "2.29.1-quest-file-uploads.17007+7890a35e3",
8867
- "@bike4mind/utils": "2.0.3-quest-file-uploads.17007+7890a35e3",
9384
+ "@bike4mind/common": "2.37.1-basic-cli-shortcuts.17058+9fe0019f0",
9385
+ "@bike4mind/mcp": "1.19.5-basic-cli-shortcuts.17058+9fe0019f0",
9386
+ "@bike4mind/services": "2.31.1-basic-cli-shortcuts.17058+9fe0019f0",
9387
+ "@bike4mind/utils": "2.0.4-basic-cli-shortcuts.17058+9fe0019f0",
8868
9388
  "@types/diff": "^5.0.9",
8869
9389
  "@types/jsonwebtoken": "^9.0.4",
8870
9390
  "@types/node": "^22.9.0",
@@ -8873,11 +9393,17 @@ var package_default = {
8873
9393
  "@types/yargs": "^17.0.32",
8874
9394
  tsup: "^8.5.1",
8875
9395
  tsx: "^4.21.0",
8876
- typescript: "^5.9.3"
9396
+ typescript: "^5.9.3",
9397
+ vitest: "^3.2.4"
8877
9398
  },
8878
- gitHead: "7890a35e398acb5486db66d00e5f4173ded0e74e"
9399
+ gitHead: "9fe0019f0bca23b4ffe22a4d9504a0e02054f48d"
8879
9400
  };
8880
9401
 
9402
+ // src/config/constants.ts
9403
+ var USAGE_DAYS = 30;
9404
+ var MODEL_NAME_COLUMN_WIDTH = 18;
9405
+ var USAGE_CACHE_TTL = 5 * 60 * 1e3;
9406
+
8881
9407
  // src/index.tsx
8882
9408
  process.removeAllListeners("warning");
8883
9409
  process.on("warning", (warning) => {
@@ -8888,9 +9414,10 @@ process.on("warning", (warning) => {
8888
9414
  });
8889
9415
  var exitTimestamp = null;
8890
9416
  var EXIT_TIMEOUT_MS = 2e3;
9417
+ var usageCache = null;
8891
9418
  function CliApp() {
8892
9419
  const { exit } = useApp();
8893
- const [state, setState] = useState3({
9420
+ const [state, setState] = useState4({
8894
9421
  session: null,
8895
9422
  sessionStore: new SessionStore(),
8896
9423
  configStore: new ConfigStore(),
@@ -8900,9 +9427,9 @@ function CliApp() {
8900
9427
  permissionManager: null,
8901
9428
  permissionPrompt: null
8902
9429
  });
8903
- const [isInitialized, setIsInitialized] = useState3(false);
8904
- const [initError, setInitError] = useState3(null);
8905
- const [commandHistory, setCommandHistory] = useState3([]);
9430
+ const [isInitialized, setIsInitialized] = useState4(false);
9431
+ const [initError, setInitError] = useState4(null);
9432
+ const [commandHistory, setCommandHistory] = useState4([]);
8906
9433
  const setStoreSession = useCliStore((state2) => state2.setSession);
8907
9434
  const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
8908
9435
  const setExitRequested = useCliStore((state2) => state2.setExitRequested);
@@ -8927,14 +9454,14 @@ function CliApp() {
8927
9454
  }
8928
9455
  await Promise.race([
8929
9456
  Promise.all(cleanupTasks),
8930
- new Promise((resolve) => setTimeout(resolve, 500))
9457
+ new Promise((resolve3) => setTimeout(resolve3, 500))
8931
9458
  // 500ms timeout
8932
9459
  ]);
8933
9460
  setTimeout(() => {
8934
9461
  process.exit(0);
8935
9462
  }, 100);
8936
9463
  }, [state.session, state.sessionStore, state.mcpManager, state.agent]);
8937
- useInput2((input, key) => {
9464
+ useInput3((input, key) => {
8938
9465
  if (key.ctrl && input === "c") {
8939
9466
  const now = Date.now();
8940
9467
  if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
@@ -9061,9 +9588,9 @@ function CliApp() {
9061
9588
  };
9062
9589
  const permissionManager = new PermissionManager(config.trustedTools || []);
9063
9590
  const promptFn = (toolName, args, preview) => {
9064
- return new Promise((resolve) => {
9591
+ return new Promise((resolve3) => {
9065
9592
  const canBeTrusted = permissionManager.canBeTrusted(toolName);
9066
- const prompt = { toolName, args, preview, canBeTrusted, resolve };
9593
+ const prompt = { toolName, args, preview, canBeTrusted, resolve: resolve3 };
9067
9594
  setState((prev) => ({
9068
9595
  ...prev,
9069
9596
  permissionManager,
@@ -9137,7 +9664,7 @@ Remember: Use context from previous messages to understand follow-up questions.`
9137
9664
  llm,
9138
9665
  model: modelInfo.id,
9139
9666
  tools: allTools,
9140
- maxIterations: 3,
9667
+ maxIterations: 10,
9141
9668
  maxTokens: config.preferences.maxTokens,
9142
9669
  temperature: config.preferences.temperature,
9143
9670
  systemPrompt: cliSystemPrompt
@@ -9252,6 +9779,7 @@ Available commands:
9252
9779
  /login - Authenticate with your B4M account
9253
9780
  /logout - Clear authentication and sign out
9254
9781
  /whoami - Show current authenticated user
9782
+ /usage - Show credit usage and balance
9255
9783
  /save <name> - Save current session
9256
9784
  /sessions - List saved sessions
9257
9785
  /config - Show configuration
@@ -9422,17 +9950,114 @@ Tool Permissions:
9422
9950
  console.log(`Expires: ${new Date(authTokens.expiresAt).toLocaleString()}`);
9423
9951
  console.log("");
9424
9952
  break;
9953
+ case "usage": {
9954
+ const usageAuthTokens = await state.configStore.getAuthTokens();
9955
+ if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
9956
+ console.log("\nNot authenticated. Run /login to authenticate.\n");
9957
+ return;
9958
+ }
9959
+ console.log("\nFetching usage data...");
9960
+ const now = Date.now();
9961
+ let currentCredits;
9962
+ let transactions;
9963
+ if (usageCache && now - usageCache.timestamp < USAGE_CACHE_TTL) {
9964
+ currentCredits = usageCache.data.currentCredits;
9965
+ transactions = usageCache.data.transactions;
9966
+ console.log("(using cached data)\n");
9967
+ } else {
9968
+ try {
9969
+ const config = await state.configStore.get();
9970
+ const apiBaseURL = getApiUrl(config.apiConfig);
9971
+ const apiClient = new ApiClient(apiBaseURL, state.configStore);
9972
+ const [identifyResponse, transactionsResponse] = await Promise.all([
9973
+ apiClient.get("/api/identify"),
9974
+ apiClient.get(`/api/credits/transactions?days=${USAGE_DAYS}`)
9975
+ ]);
9976
+ currentCredits = identifyResponse.user.currentCredits;
9977
+ transactions = transactionsResponse || [];
9978
+ usageCache = {
9979
+ data: { currentCredits, transactions },
9980
+ timestamp: now
9981
+ };
9982
+ } catch (error) {
9983
+ if (isAxiosError2(error)) {
9984
+ if (error.response?.status === 401) {
9985
+ console.log("\n\u274C Authentication failed. Run /login to re-authenticate.\n");
9986
+ } else if (error.response?.status === 403) {
9987
+ console.log("\n\u274C Access denied. You may not have permission to view usage data.\n");
9988
+ } else if (error.response?.status === 404) {
9989
+ console.log("\n\u274C Usage endpoint not available on this server.\n");
9990
+ } else if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND") {
9991
+ console.log("\n\u274C Cannot connect to server. Check your API configuration with /api-info\n");
9992
+ } else {
9993
+ console.log(`
9994
+ \u274C Error fetching usage data: ${error.message}
9995
+ `);
9996
+ }
9997
+ } else if (error instanceof Error) {
9998
+ console.log(`
9999
+ \u274C Error: ${error.message}
10000
+ `);
10001
+ } else {
10002
+ console.log("\n\u274C Unexpected error fetching usage data. Please try again.\n");
10003
+ }
10004
+ break;
10005
+ }
10006
+ }
10007
+ const deductTransactions = transactions.filter(
10008
+ (t) => CREDIT_DEDUCT_TRANSACTION_TYPES.includes(t.type)
10009
+ );
10010
+ const totalCreditsUsed = deductTransactions.reduce((sum2, t) => sum2 + Math.abs(t.credits), 0);
10011
+ const dailyBurnRate = Math.round(totalCreditsUsed / USAGE_DAYS);
10012
+ let daysRemainingStr;
10013
+ if (dailyBurnRate === 0) {
10014
+ daysRemainingStr = "N/A (no usage)";
10015
+ } else {
10016
+ const daysRemaining = Math.floor(currentCredits / dailyBurnRate);
10017
+ daysRemainingStr = `~${daysRemaining.toLocaleString()} days`;
10018
+ }
10019
+ const usageByModel = {};
10020
+ for (const t of deductTransactions) {
10021
+ const modelName = t.metadata?.modelName || ("model" in t ? t.model : null) || "Unknown";
10022
+ usageByModel[modelName] = (usageByModel[modelName] || 0) + Math.abs(t.credits);
10023
+ }
10024
+ const sortedModels = Object.entries(usageByModel).sort((a, b) => b[1] - a[1]);
10025
+ console.log("\n Credit Usage Summary");
10026
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
10027
+ console.log(`Credits Balance: ${currentCredits.toLocaleString()}`);
10028
+ console.log(`Daily Burn Rate: ${dailyBurnRate.toLocaleString()} credits/day`);
10029
+ console.log(`Days Remaining: ${daysRemainingStr}`);
10030
+ if (currentCredits === 0) {
10031
+ console.log("\n\u26A0\uFE0F You have no credits remaining.");
10032
+ console.log("\u{1F4A1} Visit bike4mind.io to purchase more credits.\n");
10033
+ break;
10034
+ }
10035
+ if (sortedModels.length > 0) {
10036
+ console.log(`
10037
+ Usage by Model (Last ${USAGE_DAYS} days):`);
10038
+ for (const [model, credits] of sortedModels) {
10039
+ const percentage = totalCreditsUsed > 0 ? Math.round(credits / totalCreditsUsed * 100) : 0;
10040
+ const paddedModel = model.padEnd(MODEL_NAME_COLUMN_WIDTH);
10041
+ console.log(` ${paddedModel}${credits.toLocaleString()} credits (${percentage}%)`);
10042
+ }
10043
+ } else {
10044
+ console.log(`
10045
+ No usage data available for the last ${USAGE_DAYS} days.`);
10046
+ }
10047
+ console.log("");
10048
+ break;
10049
+ }
9425
10050
  default:
9426
10051
  console.log(`Unknown command: /${command}`);
9427
10052
  console.log("Type /help for available commands");
9428
10053
  }
9429
10054
  };
9430
10055
  if (initError) {
9431
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React10.createElement(Text9, null, initError), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
10056
+ return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React12.createElement(Text11, null, initError), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
9432
10057
  }
9433
10058
  if (state.showLoginFlow) {
9434
10059
  const loginApiUrl = getApiUrl(state.config?.apiConfig);
9435
- return /* @__PURE__ */ React10.createElement(
10060
+ return /* @__PURE__ */ React12.createElement(
9436
10061
  LoginFlow,
9437
10062
  {
9438
10063
  apiUrl: loginApiUrl,
@@ -9455,9 +10080,9 @@ Tool Permissions:
9455
10080
  );
9456
10081
  }
9457
10082
  if (!isInitialized) {
9458
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React10.createElement(Text9, null, "\u{1F680} Initializing..."));
10083
+ return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "\u{1F680} Initializing..."));
9459
10084
  }
9460
- return /* @__PURE__ */ React10.createElement(
10085
+ return /* @__PURE__ */ React12.createElement(
9461
10086
  App,
9462
10087
  {
9463
10088
  onMessage: handleMessage,
@@ -9488,6 +10113,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
9488
10113
  if (isDevMode) {
9489
10114
  console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
9490
10115
  }
9491
- render(/* @__PURE__ */ React10.createElement(CliApp, null), {
10116
+ render(/* @__PURE__ */ React12.createElement(CliApp, null), {
9492
10117
  exitOnCtrlC: false
9493
10118
  });