@bike4mind/cli 0.2.7-basic-cli-shortcuts.17131 → 0.2.7-feat-cli-tool-lambda-v2.17129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +639 -674
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -79,13 +79,13 @@ import {
79
79
  } from "./chunk-PDX44BCA.js";
80
80
 
81
81
  // src/index.tsx
82
- import React14, { useState as useState5, useEffect as useEffect3, useCallback, useRef } from "react";
83
- import { render, Box as Box13, Text as Text13, useApp, useInput as useInput4 } from "ink";
82
+ import React13, { useState as useState4, useEffect as useEffect3, useCallback, useRef } from "react";
83
+ import { render, Box as Box13, Text as Text12, useApp, useInput as useInput3 } from "ink";
84
84
  import { v4 as uuidv49 } from "uuid";
85
85
 
86
86
  // src/components/App.tsx
87
- import React10 from "react";
88
- import { Box as Box9, Text as Text9, Static } from "ink";
87
+ import React9 from "react";
88
+ import { Box as Box9, Text as Text8, Static } from "ink";
89
89
 
90
90
  // src/components/StatusBar.tsx
91
91
  import React from "react";
@@ -95,127 +95,27 @@ var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage
95
95
  });
96
96
 
97
97
  // src/components/InputPrompt.tsx
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 && !key.meta && !key.shift) {
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 sanitizedInput = input === "\r" ? "\n" : input;
185
- const newValue = value.slice(0, cursorOffset) + sanitizedInput + value.slice(cursorOffset);
186
- onChange(newValue);
187
- setCursorOffset(cursorOffset + 1);
188
- }
189
- },
190
- { isActive: true }
191
- );
192
- const hasValue = value.length > 0;
193
- if (!hasValue) {
194
- 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));
195
- }
196
- const beforeCursor = value.slice(0, cursorOffset);
197
- const cursorChar = value[cursorOffset] || " ";
198
- const afterCursor = value.slice(cursorOffset + 1);
199
- return /* @__PURE__ */ React2.createElement(Text2, null, beforeCursor, showCursor && /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, cursorChar), !showCursor && cursorChar, afterCursor);
200
- }
98
+ import React4, { useState, useMemo, useEffect } from "react";
99
+ import { Box as Box4, Text as Text4, useInput } from "ink";
100
+ import TextInput from "ink-text-input";
201
101
 
202
102
  // src/components/CommandAutocomplete.tsx
203
- import React3 from "react";
204
- import { Box as Box2, Text as Text3 } from "ink";
103
+ import React2 from "react";
104
+ import { Box as Box2, Text as Text2 } from "ink";
205
105
  function CommandAutocomplete({ commands, selectedIndex }) {
206
106
  if (commands.length === 0) {
207
- return /* @__PURE__ */ React3.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching commands"));
107
+ return /* @__PURE__ */ React2.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "No matching commands"));
208
108
  }
209
- 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
+ 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) => {
210
110
  const args = cmd.args ? ` ${cmd.args}` : "";
211
111
  const isSelected = index === selectedIndex;
212
- 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
+ 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));
213
113
  }));
214
114
  }
215
115
 
216
116
  // src/components/FileAutocomplete.tsx
217
- import React4 from "react";
218
- import { Box as Box3, Text as Text4 } from "ink";
117
+ import React3 from "react";
118
+ import { Box as Box3, Text as Text3 } from "ink";
219
119
 
220
120
  // src/utils/fileSearch.ts
221
121
  import * as fs from "fs";
@@ -404,14 +304,14 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
404
304
  // src/components/FileAutocomplete.tsx
405
305
  function FileAutocomplete({ files, selectedIndex, query }) {
406
306
  if (files.length === 0) {
407
- return /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
307
+ return /* @__PURE__ */ React3.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
408
308
  }
409
- 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) => {
309
+ return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, files.length === 1 ? "1 match" : `${files.length} matches`, " - Use up/down to navigate, Tab to select")), files.map((file, index) => {
410
310
  const isSelected = index === selectedIndex;
411
311
  const icon = file.isDirectory ? "[folder]" : "[file] ";
412
312
  const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
413
313
  const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
414
- 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));
314
+ return /* @__PURE__ */ React3.createElement(Box3, { key: file.path, marginLeft: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "> " : " "), /* @__PURE__ */ React3.createElement(Text3, { color: file.isDirectory ? "yellow" : isSelected ? "cyan" : void 0, bold: isSelected }, icon), /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, " ", pathDisplay), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, sizeDisplay));
415
315
  }));
416
316
  }
417
317
 
@@ -726,12 +626,13 @@ function findAtTrigger(value) {
726
626
  return null;
727
627
  }
728
628
  function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = [] }) {
729
- const [value, setValue] = useState2("");
730
- const [selectedIndex, setSelectedIndex] = useState2(0);
731
- const [historyIndex, setHistoryIndex] = useState2(-1);
732
- const [tempInput, setTempInput] = useState2("");
733
- const [fileAutocomplete, setFileAutocomplete] = useState2(null);
734
- const [fileSelectedIndex, setFileSelectedIndex] = useState2(0);
629
+ const [value, setValue] = useState("");
630
+ const [selectedIndex, setSelectedIndex] = useState(0);
631
+ const [historyIndex, setHistoryIndex] = useState(-1);
632
+ const [tempInput, setTempInput] = useState("");
633
+ const [inputKey, setInputKey] = useState(0);
634
+ const [fileAutocomplete, setFileAutocomplete] = useState(null);
635
+ const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
735
636
  const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
736
637
  const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
737
638
  const filteredCommands = useMemo(() => {
@@ -748,7 +649,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
748
649
  useEffect(() => {
749
650
  setFileSelectedIndex(0);
750
651
  }, [filteredFiles]);
751
- useInput2((_input, key) => {
652
+ useInput((input, key) => {
752
653
  if (fileAutocomplete?.active && filteredFiles.length > 0) {
753
654
  if (key.upArrow) {
754
655
  setFileSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredFiles.length - 1);
@@ -805,6 +706,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
805
706
  const pathToInsert = file.isDirectory ? `@${file.path}/` : `@${file.path}`;
806
707
  const newValue = beforeAt + pathToInsert + afterQuery;
807
708
  setValue(newValue);
709
+ setInputKey((prev) => prev + 1);
808
710
  if (file.isDirectory) {
809
711
  setFileAutocomplete({
810
712
  active: true,
@@ -831,6 +733,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
831
733
  if (selectedCommand.args) {
832
734
  setValue(commandText + " ");
833
735
  setHistoryIndex(-1);
736
+ setInputKey((prev) => prev + 1);
834
737
  return;
835
738
  }
836
739
  setValue("");
@@ -849,6 +752,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
849
752
  if (imageEvent && onImageDetected) {
850
753
  const placeholder = await onImageDetected(imageEvent.data);
851
754
  setValue(`${placeholder} `);
755
+ setInputKey((prev) => prev + 1);
852
756
  return;
853
757
  }
854
758
  }
@@ -868,28 +772,29 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
868
772
  setFileAutocomplete(null);
869
773
  }
870
774
  };
871
- return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(
872
- CustomTextInput,
775
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React4.createElement(
776
+ TextInput,
873
777
  {
778
+ key: inputKey,
874
779
  value,
875
780
  onChange: handleChange,
876
781
  onSubmit: handleSubmit,
877
782
  placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
878
783
  showCursor: !disabled
879
784
  }
880
- )), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
785
+ )), shouldShowCommandAutocomplete && /* @__PURE__ */ React4.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React4.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
881
786
  }
882
787
 
883
788
  // src/components/AgentThinking.tsx
884
- import React7 from "react";
789
+ import React6 from "react";
885
790
  import { Box as Box6 } from "ink";
886
791
 
887
792
  // src/components/ThoughtStream.tsx
888
- import React6 from "react";
889
- import { Box as Box5, Text as Text6 } from "ink";
793
+ import React5 from "react";
794
+ import { Box as Box5, Text as Text5 } from "ink";
890
795
  import Spinner from "ink-spinner";
891
- var ThoughtStream = React6.memo(function ThoughtStream2({ steps }) {
892
- 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...")));
796
+ var ThoughtStream = React5.memo(function ThoughtStream2({ steps }) {
797
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, steps.map((step, index) => /* @__PURE__ */ React5.createElement(Box5, { key: index, flexDirection: "column" }, step.type === "thought" && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, "\u{1F914} Thought: "), /* @__PURE__ */ React5.createElement(Text5, null, step.content)), step.type === "action" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, "\u26A1 Action: "), /* @__PURE__ */ React5.createElement(Text5, { bold: true }, step.metadata?.toolName || "unknown")), step.metadata?.toolInput && /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, JSON.stringify(step.metadata.toolInput, null, 2)))), step.type === "observation" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, "\u{1F4CA} Observation: ")), /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, step.content))), step.type === "final_answer" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green", bold: true }, "\u2705 Final Answer:", " ")), /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, step.content)), step.metadata?.tokenUsage && /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(", step.metadata.tokenUsage.total, " tokens)"))))), !steps.some((s) => s.type === "final_answer") && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, /* @__PURE__ */ React5.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " ", steps.length === 0 ? "Thinking..." : "Processing...")));
893
798
  });
894
799
 
895
800
  // src/store/index.ts
@@ -918,32 +823,32 @@ var useCliStore = create((set) => ({
918
823
  }));
919
824
 
920
825
  // src/components/AgentThinking.tsx
921
- var AgentThinking = React7.memo(function AgentThinking2() {
826
+ var AgentThinking = React6.memo(function AgentThinking2() {
922
827
  const isThinking = useCliStore((state) => state.isThinking);
923
828
  const agentSteps = useCliStore((state) => state.agentSteps);
924
829
  if (!isThinking) {
925
830
  return null;
926
831
  }
927
- return /* @__PURE__ */ React7.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
832
+ return /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React6.createElement(ThoughtStream, { steps: agentSteps }));
928
833
  });
929
834
 
930
835
  // src/components/PermissionPrompt.tsx
931
- import React8 from "react";
932
- import { Box as Box7, Text as Text7 } from "ink";
836
+ import React7 from "react";
837
+ import { Box as Box7, Text as Text6 } from "ink";
933
838
  import SelectInput from "ink-select-input";
934
839
  function renderDiffPreview(preview) {
935
840
  const lines = preview.split("\n");
936
841
  return lines.map((line, index) => {
937
842
  if (line.startsWith("+")) {
938
- return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "green" }, line);
843
+ return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "green" }, line);
939
844
  }
940
845
  if (line.startsWith("-")) {
941
- return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "red" }, line);
846
+ return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "red" }, line);
942
847
  }
943
848
  if (line.startsWith("@@")) {
944
- return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "cyan" }, line);
849
+ return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "cyan" }, line);
945
850
  }
946
- return /* @__PURE__ */ React8.createElement(Text7, { key: index, dimColor: true }, line);
851
+ return /* @__PURE__ */ React7.createElement(Text6, { key: index, dimColor: true }, line);
947
852
  });
948
853
  }
949
854
  function PermissionPrompt({
@@ -963,12 +868,12 @@ function PermissionPrompt({
963
868
  { label: "\u2717 Deny", value: "deny" }
964
869
  ];
965
870
  const argsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
966
- 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) })));
871
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Tool: "), /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Action: "), /* @__PURE__ */ React7.createElement(Text6, null, toolDescription)), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Arguments:"), /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Preview:"), /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
967
872
  }
968
873
 
969
874
  // src/components/ConfigEditor.tsx
970
- import React9, { useState as useState3, useMemo as useMemo2 } from "react";
971
- import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
875
+ import React8, { useState as useState2, useMemo as useMemo2 } from "react";
876
+ import { Box as Box8, Text as Text7, useInput as useInput2 } from "ink";
972
877
  var MAX_ITERATIONS_OPTIONS = [
973
878
  { label: "10", value: 10 },
974
879
  { label: "20", value: 20 },
@@ -1099,10 +1004,10 @@ function buildConfigItems(availableModels) {
1099
1004
  return items;
1100
1005
  }
1101
1006
  function ConfigEditor({ config, availableModels, onSave, onClose }) {
1102
- const [selectedIndex, setSelectedIndex] = useState3(0);
1103
- const [editedConfig, setEditedConfig] = useState3(config);
1104
- const [saveError, setSaveError] = useState3(null);
1105
- const [isSaving, setIsSaving] = useState3(false);
1007
+ const [selectedIndex, setSelectedIndex] = useState2(0);
1008
+ const [editedConfig, setEditedConfig] = useState2(config);
1009
+ const [saveError, setSaveError] = useState2(null);
1010
+ const [isSaving, setIsSaving] = useState2(false);
1106
1011
  const configItems = useMemo2(() => buildConfigItems(availableModels), [availableModels]);
1107
1012
  const hasChanges = useMemo2(() => {
1108
1013
  return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
@@ -1122,7 +1027,7 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
1122
1027
  }
1123
1028
  onClose();
1124
1029
  };
1125
- useInput3((input, key) => {
1030
+ useInput2((input, key) => {
1126
1031
  const currentItem = configItems[selectedIndex];
1127
1032
  if (key.upArrow) {
1128
1033
  setSelectedIndex((prev) => prev > 0 ? prev - 1 : configItems.length - 1);
@@ -1187,13 +1092,13 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
1187
1092
  }
1188
1093
  return String(value);
1189
1094
  };
1190
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1 }, /* @__PURE__ */ React9.createElement(Box8, { justifyContent: "space-between", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "Settings"), isSaving ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "Saving...") : hasChanges ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, color: "yellow" }, "(unsaved changes)") : null), saveError && /* @__PURE__ */ React9.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, "Error: ", saveError)), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, configItems.map((item, index) => {
1095
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1 }, /* @__PURE__ */ React8.createElement(Box8, { justifyContent: "space-between", marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, "Settings"), isSaving ? /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "Saving...") : hasChanges ? /* @__PURE__ */ React8.createElement(Text7, { dimColor: true, color: "yellow" }, "(unsaved changes)") : null), saveError && /* @__PURE__ */ React8.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red" }, "Error: ", saveError)), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, configItems.map((item, index) => {
1191
1096
  const isSelected = index === selectedIndex;
1192
1097
  const value = item.getValue(editedConfig);
1193
1098
  const displayValue = formatValue(item, value);
1194
1099
  const hasOptions = item.type === "select" || item.type === "boolean" || item.type === "number";
1195
- return /* @__PURE__ */ React9.createElement(Box8, { key: item.key }, /* @__PURE__ */ React9.createElement(Text8, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React9.createElement(Box8, { width: 18 }, /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "white" : "gray" }, item.label, ":")), /* @__PURE__ */ React9.createElement(Box8, null, hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "< "), /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "green" : void 0 }, displayValue), hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " >")));
1196
- })), /* @__PURE__ */ React9.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
1100
+ return /* @__PURE__ */ React8.createElement(Box8, { key: item.key }, /* @__PURE__ */ React8.createElement(Text7, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React8.createElement(Box8, { width: 18 }, /* @__PURE__ */ React8.createElement(Text7, { bold: isSelected, color: isSelected ? "white" : "gray" }, item.label, ":")), /* @__PURE__ */ React8.createElement(Box8, null, hasOptions && isSelected && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "< "), /* @__PURE__ */ React8.createElement(Text7, { bold: isSelected, color: isSelected ? "green" : void 0 }, displayValue), hasOptions && isSelected && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " >")));
1101
+ })), /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
1197
1102
  }
1198
1103
 
1199
1104
  // src/utils/processFileReferences.ts
@@ -1331,7 +1236,7 @@ function App({
1331
1236
  const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
1332
1237
  const exitRequested = useCliStore((state) => state.exitRequested);
1333
1238
  const setIsThinking = useCliStore((state) => state.setIsThinking);
1334
- const handleSubmit = React10.useCallback(
1239
+ const handleSubmit = React9.useCallback(
1335
1240
  async (input) => {
1336
1241
  const trimmed = input.trim();
1337
1242
  if (!trimmed) return;
@@ -1360,7 +1265,7 @@ ${errorBlock}`;
1360
1265
  },
1361
1266
  [onMessage, onCommand, setIsThinking]
1362
1267
  );
1363
- return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
1268
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
1364
1269
  ConfigEditor,
1365
1270
  {
1366
1271
  config,
@@ -1368,7 +1273,7 @@ ${errorBlock}`;
1368
1273
  onSave: onSaveConfig,
1369
1274
  onClose: () => setShowConfigEditor(false)
1370
1275
  }
1371
- )) : permissionPrompt ? /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
1276
+ )) : permissionPrompt ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
1372
1277
  PermissionPrompt,
1373
1278
  {
1374
1279
  toolName: permissionPrompt.toolName,
@@ -1377,7 +1282,7 @@ ${errorBlock}`;
1377
1282
  canBeTrusted: permissionPrompt.canBeTrusted,
1378
1283
  onResponse: onPermissionResponse
1379
1284
  }
1380
- )) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(MessageItem, { message }))), /* @__PURE__ */ React10.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
1285
+ )) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React9.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(MessageItem, { message }))), /* @__PURE__ */ React9.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React9.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React9.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
1381
1286
  InputPrompt,
1382
1287
  {
1383
1288
  onSubmit: handleSubmit,
@@ -1388,36 +1293,36 @@ ${errorBlock}`;
1388
1293
  ))));
1389
1294
  }
1390
1295
  function MessageItem({ message }) {
1391
- return /* @__PURE__ */ React10.createElement(Box9, { 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 ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, 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) => {
1296
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Box9, 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(Box9, { 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(Box9, { 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) => {
1392
1297
  if (step.type === "thought") {
1393
- 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(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
1298
+ return /* @__PURE__ */ React9.createElement(Box9, { 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 ? "..." : ""}`));
1394
1299
  }
1395
1300
  if (step.type === "action") {
1396
1301
  const toolName = step.metadata?.toolName || "unknown";
1397
1302
  const toolInput = step.metadata?.toolInput;
1398
1303
  const observationStep = message.metadata.steps[idx + 1];
1399
1304
  const result = observationStep?.type === "observation" ? observationStep.content : null;
1400
- 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 ? "..." : ""}`));
1305
+ return /* @__PURE__ */ React9.createElement(Box9, { 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 ? "..." : ""}`));
1401
1306
  }
1402
1307
  return null;
1403
- }).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`)));
1308
+ }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1404
1309
  }
1405
1310
 
1406
1311
  // src/components/MessageList.tsx
1407
- import React11 from "react";
1408
- import { Box as Box10, Text as Text10 } from "ink";
1312
+ import React10 from "react";
1313
+ import { Box as Box10, Text as Text9 } from "ink";
1409
1314
  function stripThinkingTags(content) {
1410
1315
  return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
1411
1316
  }
1412
- var MessageList = React11.memo(
1317
+ var MessageList = React10.memo(
1413
1318
  function MessageList2({ messages }) {
1414
1319
  if (messages.length === 0) {
1415
- return /* @__PURE__ */ React11.createElement(Box10, { paddingY: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "No messages yet. Type a message to start!"));
1320
+ return /* @__PURE__ */ React10.createElement(Box10, { paddingY: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "No messages yet. Type a message to start!"));
1416
1321
  }
1417
- return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React11.createElement(Box10, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React11.createElement(Text10, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { 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__ */ React11.createElement(Text10, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1322
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React10.createElement(Box10, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box10, 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(Box10, { 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(Box10, { 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) => {
1418
1323
  if (step.type === "thought") {
1419
- return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React11.createElement(
1420
- Text10,
1324
+ return /* @__PURE__ */ React10.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(
1325
+ Text9,
1421
1326
  {
1422
1327
  dimColor: true
1423
1328
  },
@@ -1429,10 +1334,10 @@ var MessageList = React11.memo(
1429
1334
  const toolInput = step.metadata?.toolInput;
1430
1335
  const observationStep = message.metadata.steps[idx + 1];
1431
1336
  const result = observationStep?.type === "observation" ? observationStep.content : null;
1432
- return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React11.createElement(Text10, { 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 ? "..." : ""}`));
1337
+ return /* @__PURE__ */ React10.createElement(Box10, { 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 ? "..." : ""}`));
1433
1338
  }
1434
1339
  return null;
1435
- }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React11.createElement(Box10, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
1340
+ }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box10, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
1436
1341
  },
1437
1342
  (prevProps, nextProps) => {
1438
1343
  if (prevProps.messages.length !== nextProps.messages.length) {
@@ -1443,8 +1348,8 @@ var MessageList = React11.memo(
1443
1348
  );
1444
1349
 
1445
1350
  // src/components/TrustLocationSelector.tsx
1446
- import React12 from "react";
1447
- import { Box as Box11, Text as Text11 } from "ink";
1351
+ import React11 from "react";
1352
+ import { Box as Box11, Text as Text10 } from "ink";
1448
1353
  import SelectInput2 from "ink-select-input";
1449
1354
  function TrustLocationSelector({ inProject, onSelect, onCancel }) {
1450
1355
  const items = [];
@@ -1465,19 +1370,19 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
1465
1370
  const handleSelect = (item) => {
1466
1371
  onSelect(item.value);
1467
1372
  };
1468
- return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React12.createElement(
1373
+ return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React11.createElement(
1469
1374
  SelectInput2,
1470
1375
  {
1471
1376
  items,
1472
1377
  onSelect: handleSelect,
1473
- itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
1378
+ itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
1474
1379
  }
1475
- ), /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
1380
+ ), /* @__PURE__ */ React11.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
1476
1381
  }
1477
1382
 
1478
1383
  // src/components/LoginFlow.tsx
1479
- import React13, { useState as useState4, useEffect as useEffect2 } from "react";
1480
- import { Box as Box12, Text as Text12 } from "ink";
1384
+ import React12, { useState as useState3, useEffect as useEffect2 } from "react";
1385
+ import { Box as Box12, Text as Text11 } from "ink";
1481
1386
  import Spinner2 from "ink-spinner";
1482
1387
  import jwt from "jsonwebtoken";
1483
1388
  import open from "open";
@@ -1593,10 +1498,10 @@ var OAuthClient = class {
1593
1498
 
1594
1499
  // src/components/LoginFlow.tsx
1595
1500
  function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
1596
- const [status, setStatus] = useState4("initiating");
1597
- const [deviceFlow, setDeviceFlow] = useState4(null);
1598
- const [statusMessage, setStatusMessage] = useState4("Initiating device authorization...");
1599
- const [error, setError] = useState4(null);
1501
+ const [status, setStatus] = useState3("initiating");
1502
+ const [deviceFlow, setDeviceFlow] = useState3(null);
1503
+ const [statusMessage, setStatusMessage] = useState3("Initiating device authorization...");
1504
+ const [error, setError] = useState3(null);
1600
1505
  useEffect2(() => {
1601
1506
  const runLoginFlow = async () => {
1602
1507
  const oauth = new OAuthClient(apiUrl);
@@ -1640,15 +1545,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
1640
1545
  }
1641
1546
  }, [deviceFlow, status]);
1642
1547
  if (status === "initiating") {
1643
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
1548
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, /* @__PURE__ */ React12.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
1644
1549
  }
1645
1550
  if (status === "error") {
1646
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: "red" }, error)));
1551
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: "red" }, error)));
1647
1552
  }
1648
1553
  if (status === "success") {
1649
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "You can now use B4M CLI with your account.")));
1554
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "You can now use B4M CLI with your account.")));
1650
1555
  }
1651
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "And enter this code when prompted:")), /* @__PURE__ */ React13.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
1556
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "And enter this code when prompted:")), /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, /* @__PURE__ */ React12.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
1652
1557
  }
1653
1558
 
1654
1559
  // src/storage/SessionStore.ts
@@ -8858,144 +8763,464 @@ Last modified: ${stats.mtime.toLocaleString()}`;
8858
8763
  }
8859
8764
  }
8860
8765
 
8861
- // src/utils/toolsAdapter.ts
8862
- var NoOpStorage = class extends BaseStorage {
8863
- async upload(input, destination, options) {
8864
- return `/tmp/${destination}`;
8865
- }
8866
- async download(path13) {
8867
- throw new Error("Download not supported in CLI");
8868
- }
8869
- async delete(path13) {
8766
+ // src/utils/Logger.ts
8767
+ import fs10 from "fs/promises";
8768
+ import path11 from "path";
8769
+ import os from "os";
8770
+ var Logger2 = class _Logger {
8771
+ constructor() {
8772
+ this.logFilePath = null;
8773
+ this.sessionId = null;
8774
+ this.fileLoggingEnabled = true;
8775
+ this.consoleVerbose = false;
8870
8776
  }
8871
- async getSignedUrl(path13) {
8872
- return `/tmp/${path13}`;
8777
+ static {
8778
+ this.instance = null;
8873
8779
  }
8874
- getPublicUrl(path13) {
8875
- return `/tmp/${path13}`;
8780
+ static getInstance() {
8781
+ if (!_Logger.instance) {
8782
+ _Logger.instance = new _Logger();
8783
+ }
8784
+ return _Logger.instance;
8876
8785
  }
8877
- async getPreview(path13) {
8878
- return `/tmp/${path13}`;
8786
+ /**
8787
+ * Initialize the logger with a session ID
8788
+ */
8789
+ async initialize(sessionId) {
8790
+ this.sessionId = sessionId;
8791
+ const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
8792
+ await fs10.mkdir(debugDir, { recursive: true });
8793
+ this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
8794
+ await this.writeToFile("INFO", "=== CLI SESSION START ===");
8879
8795
  }
8880
- async getMetadata(path13) {
8881
- return { size: 0, contentType: "application/octet-stream" };
8796
+ /**
8797
+ * Set whether verbose console logging is enabled
8798
+ */
8799
+ setVerbose(enabled) {
8800
+ this.consoleVerbose = enabled;
8882
8801
  }
8883
- };
8884
- var CliLogger = class extends Logger {
8885
- constructor() {
8886
- super({ logInJson: false });
8802
+ /**
8803
+ * Set whether file logging is enabled
8804
+ */
8805
+ setFileLoggingEnabled(enabled) {
8806
+ this.fileLoggingEnabled = enabled;
8887
8807
  }
8888
- // Override all logging methods to be silent
8889
- info(...args) {
8808
+ /**
8809
+ * DEBUG level - verbose-only console, always file
8810
+ */
8811
+ debug(message) {
8812
+ this.writeToFile("DEBUG", message).catch(() => {
8813
+ });
8814
+ if (this.consoleVerbose) {
8815
+ console.log(message);
8816
+ }
8890
8817
  }
8891
- warn(...args) {
8818
+ /**
8819
+ * INFO level - always shown to user
8820
+ */
8821
+ info(message) {
8822
+ this.writeToFile("INFO", message).catch(() => {
8823
+ });
8824
+ console.log(message);
8892
8825
  }
8893
- error(...args) {
8826
+ /**
8827
+ * WARN level - always shown to user
8828
+ */
8829
+ warn(message) {
8830
+ this.writeToFile("WARN", message).catch(() => {
8831
+ });
8832
+ console.warn(message);
8894
8833
  }
8895
- debug(...args) {
8834
+ /**
8835
+ * ERROR level - always shown to user
8836
+ */
8837
+ error(message, err) {
8838
+ this.writeToFile("ERROR", message).catch(() => {
8839
+ });
8840
+ if (err) {
8841
+ this.logErrorDetailsToFile(err).catch(() => {
8842
+ });
8843
+ }
8844
+ console.error(message);
8896
8845
  }
8897
- log(...args) {
8846
+ /**
8847
+ * Write log entry to file
8848
+ */
8849
+ async writeToFile(level, message) {
8850
+ if (!this.fileLoggingEnabled || !this.logFilePath) {
8851
+ return;
8852
+ }
8853
+ try {
8854
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
8855
+ const logEntry = `[${timestamp}] [${level}] ${message}
8856
+ `;
8857
+ await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
8858
+ } catch (error) {
8859
+ console.error("File logging failed:", error);
8860
+ }
8898
8861
  }
8899
- };
8900
- function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore) {
8901
- const originalFn = tool.toolFn;
8902
- const toolName = tool.toolSchema.name;
8903
- return {
8904
- ...tool,
8905
- toolFn: async (args) => {
8906
- if (!permissionManager.needsPermission(toolName)) {
8907
- const result2 = await originalFn(args);
8908
- agentContext.observationQueue.push({ toolName, result: result2 });
8909
- return result2;
8910
- }
8911
- let preview;
8912
- if (toolName === "edit_file" && args?.path && args?.content) {
8913
- try {
8914
- preview = await generateFileDiffPreview({
8915
- path: args.path,
8916
- content: args.content
8917
- });
8918
- } catch (error) {
8919
- preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
8920
- }
8921
- } else if (toolName === "create_file" && args?.path && args?.content) {
8922
- try {
8923
- preview = await generateFileDiffPreview({
8924
- path: args.path,
8925
- content: args.content
8926
- });
8927
- } catch (error) {
8928
- preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
8929
- }
8930
- } else if (toolName === "delete_file" && args?.path) {
8931
- try {
8932
- preview = await generateFileDeletePreview({
8933
- path: args.path
8934
- });
8935
- } catch (error) {
8936
- preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
8937
- }
8938
- }
8939
- const response = await showPermissionPrompt(toolName, args, preview);
8940
- if (response.action === "deny") {
8941
- throw new PermissionDeniedError(toolName, args);
8942
- }
8943
- if (response.action === "allow-always") {
8944
- const canTrust = permissionManager.trustTool(toolName);
8945
- if (canTrust) {
8946
- const projectDir = configStore.getProjectConfigDir();
8947
- if (projectDir) {
8948
- try {
8949
- await configStore.initProjectConfig();
8950
- const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
8951
- const updatedLocal = {
8952
- ...existingLocal,
8953
- trustedTools: [...existingLocal.trustedTools || [], toolName]
8954
- };
8955
- await configStore.saveProjectLocalConfig(updatedLocal);
8956
- } catch (error) {
8957
- await configStore.trustTool(toolName);
8862
+ /**
8863
+ * Log error details to file
8864
+ */
8865
+ async logErrorDetailsToFile(err) {
8866
+ if (!this.fileLoggingEnabled || !this.logFilePath) {
8867
+ return;
8868
+ }
8869
+ try {
8870
+ if (err && typeof err === "object" && "response" in err && err.response) {
8871
+ const response = err.response;
8872
+ const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
8873
+ await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
8874
+ await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
8875
+ await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
8876
+ if (response.data) {
8877
+ const errorText = this.extractErrorMessage(response.data);
8878
+ if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
8879
+ await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
8880
+ const parsedError = this.parseHtmlError(errorText);
8881
+ if (parsedError) {
8882
+ await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
8958
8883
  }
8884
+ await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
8959
8885
  } else {
8960
- await configStore.trustTool(toolName);
8886
+ const preview = this.truncate(errorText, 500);
8887
+ await this.writeToFile("ERROR", ` Response: ${preview}`);
8961
8888
  }
8889
+ } else {
8890
+ await this.writeToFile("ERROR", ` Response: (no data)`);
8962
8891
  }
8963
8892
  }
8964
- const result = await originalFn(args);
8965
- agentContext.observationQueue.push({ toolName, result });
8966
- return result;
8893
+ if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
8894
+ const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
8895
+ await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
8896
+ } else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
8897
+ await this.writeToFile("ERROR", ` Message: ${err.message}`);
8898
+ }
8899
+ } catch (error) {
8967
8900
  }
8968
- };
8969
- }
8970
- function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, toolApiKeys) {
8971
- const logger2 = new CliLogger();
8972
- const storage = new NoOpStorage();
8973
- const user = {
8974
- _id: userId,
8975
- email: "cli-user@bike4mind.local",
8976
- firstName: "CLI",
8977
- lastName: "User"
8978
- };
8979
- const statusUpdate = async (q, status) => {
8980
- };
8901
+ }
8902
+ /**
8903
+ * Safely stringify object, handling circular references
8904
+ */
8905
+ safeStringify(obj) {
8906
+ try {
8907
+ return JSON.stringify(obj);
8908
+ } catch (error) {
8909
+ if (error instanceof Error && error.message.includes("circular")) {
8910
+ try {
8911
+ const seen = /* @__PURE__ */ new WeakSet();
8912
+ return JSON.stringify(obj, (key, value) => {
8913
+ if (typeof value === "object" && value !== null) {
8914
+ if (seen.has(value)) {
8915
+ return "[Circular]";
8916
+ }
8917
+ seen.add(value);
8918
+ }
8919
+ return value;
8920
+ });
8921
+ } catch {
8922
+ return "[Unable to stringify]";
8923
+ }
8924
+ }
8925
+ return "[Stringify error]";
8926
+ }
8927
+ }
8928
+ /**
8929
+ * Extract readable text from error response data
8930
+ */
8931
+ extractErrorMessage(data) {
8932
+ if (Buffer.isBuffer(data)) {
8933
+ return data.toString("utf-8");
8934
+ }
8935
+ if (typeof data === "string") {
8936
+ return data;
8937
+ }
8938
+ if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
8939
+ const chunks = [];
8940
+ for (const chunk of data._readableState.buffer) {
8941
+ if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
8942
+ chunks.push(Buffer.from(chunk.data));
8943
+ } else if (Buffer.isBuffer(chunk)) {
8944
+ chunks.push(chunk);
8945
+ } else if (chunk && typeof chunk === "object" && "data" in chunk) {
8946
+ if (Buffer.isBuffer(chunk.data)) {
8947
+ chunks.push(chunk.data);
8948
+ } else if (Array.isArray(chunk.data)) {
8949
+ chunks.push(Buffer.from(chunk.data));
8950
+ }
8951
+ }
8952
+ }
8953
+ if (chunks.length > 0) {
8954
+ return Buffer.concat(chunks).toString("utf-8");
8955
+ }
8956
+ }
8957
+ return this.safeStringify(data);
8958
+ }
8959
+ /**
8960
+ * Parse HTML error page to extract error message
8961
+ */
8962
+ parseHtmlError(html) {
8963
+ const titleMatch = html.match(/<title>(.*?)<\/title>/i);
8964
+ const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
8965
+ const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
8966
+ if (titleMatch && titleMatch[1] !== "Error") {
8967
+ return titleMatch[1].trim();
8968
+ }
8969
+ if (h1Match) {
8970
+ return h1Match[1].trim();
8971
+ }
8972
+ if (bodyMatch) {
8973
+ const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8974
+ return text.substring(0, 200);
8975
+ }
8976
+ return null;
8977
+ }
8978
+ /**
8979
+ * Truncate string to max length with ellipsis
8980
+ */
8981
+ truncate(str, maxLength) {
8982
+ if (str.length <= maxLength) {
8983
+ return str;
8984
+ }
8985
+ return str.substring(0, maxLength) + "... [truncated]";
8986
+ }
8987
+ /**
8988
+ * Format bytes to human-readable size
8989
+ */
8990
+ formatBytes(bytes) {
8991
+ if (bytes === 0) return "0 B";
8992
+ const k = 1024;
8993
+ const sizes = ["B", "KB", "MB", "GB"];
8994
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
8995
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
8996
+ }
8997
+ /**
8998
+ * Clean up old debug logs (older than 30 days)
8999
+ */
9000
+ async cleanupOldLogs() {
9001
+ if (!this.fileLoggingEnabled) return;
9002
+ try {
9003
+ const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9004
+ const files = await fs10.readdir(debugDir);
9005
+ const now = Date.now();
9006
+ const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
9007
+ for (const file of files) {
9008
+ const filePath = path11.join(debugDir, file);
9009
+ const stats = await fs10.stat(filePath);
9010
+ if (stats.mtime.getTime() < thirtyDaysAgo) {
9011
+ await fs10.unlink(filePath);
9012
+ }
9013
+ }
9014
+ } catch (error) {
9015
+ console.error("Failed to cleanup old logs:", error);
9016
+ }
9017
+ }
9018
+ };
9019
+ var logger = Logger2.getInstance();
9020
+
9021
+ // src/llm/ServerToolExecutor.ts
9022
+ var ServerToolExecutor = class {
9023
+ constructor(apiClient) {
9024
+ this.toolsEndpoint = "/api/ai/v1/tools";
9025
+ this.apiClient = apiClient;
9026
+ }
9027
+ /**
9028
+ * Execute a tool server-side
9029
+ *
9030
+ * @param toolName - Name of the tool to execute (e.g., 'weather_info', 'web_search')
9031
+ * @param input - Tool input parameters
9032
+ * @returns Tool execution result (content or error)
9033
+ */
9034
+ async executeTool(toolName, input) {
9035
+ logger.debug(`[ServerToolExecutor] Executing tool: ${toolName}`);
9036
+ logger.debug(`[ServerToolExecutor] Endpoint: ${this.toolsEndpoint}`);
9037
+ logger.debug(`[ServerToolExecutor] Input: ${JSON.stringify(input)}`);
9038
+ try {
9039
+ const result = await this.apiClient.post(this.toolsEndpoint, {
9040
+ toolName,
9041
+ input
9042
+ });
9043
+ logger.debug(`[ServerToolExecutor] Response: ${JSON.stringify(result)}`);
9044
+ if (!result.success) {
9045
+ const errorMessage = result.error || "Tool execution failed";
9046
+ logger.error(`[ServerToolExecutor] Tool ${toolName} failed: ${errorMessage}`);
9047
+ return `Error executing ${toolName}: ${errorMessage}`;
9048
+ }
9049
+ logger.debug(`[ServerToolExecutor] Tool ${toolName} succeeded in ${result.executionTimeMs}ms`);
9050
+ return result.content || "";
9051
+ } catch (error) {
9052
+ const err = error;
9053
+ const errorMessage = err?.response?.data?.error || err?.message || "Unknown error";
9054
+ const status = err?.response?.status || "unknown";
9055
+ logger.error(`[ServerToolExecutor] Request failed for ${toolName}: ${errorMessage} (status: ${status})`);
9056
+ logger.error(`[ServerToolExecutor] Full error: ${JSON.stringify(err, null, 2)}`);
9057
+ return `Error executing ${toolName}: ${errorMessage}`;
9058
+ }
9059
+ }
9060
+ };
9061
+
9062
+ // src/llm/ToolRouter.ts
9063
+ var SERVER_TOOLS = ["weather_info", "web_search"];
9064
+ var LOCAL_TOOLS = [
9065
+ "file_read",
9066
+ "create_file",
9067
+ "glob_files",
9068
+ "grep_search",
9069
+ "delete_file",
9070
+ "edit_file",
9071
+ "dice_roll",
9072
+ "math_evaluate",
9073
+ "current_datetime"
9074
+ ];
9075
+ function isServerTool(toolName) {
9076
+ return SERVER_TOOLS.includes(toolName);
9077
+ }
9078
+ function isLocalTool(toolName) {
9079
+ return LOCAL_TOOLS.includes(toolName);
9080
+ }
9081
+ async function executeTool(toolName, input, apiClient, localToolFn) {
9082
+ if (isServerTool(toolName)) {
9083
+ logger.debug(`[ToolRouter] Routing ${toolName} to server`);
9084
+ const executor = new ServerToolExecutor(apiClient);
9085
+ return await executor.executeTool(toolName, input);
9086
+ } else if (isLocalTool(toolName)) {
9087
+ logger.debug(`[ToolRouter] Executing ${toolName} locally`);
9088
+ if (!localToolFn) {
9089
+ throw new Error(`Local tool ${toolName} has no implementation`);
9090
+ }
9091
+ const result = await localToolFn(input);
9092
+ return typeof result === "string" ? result : JSON.stringify(result);
9093
+ } else {
9094
+ throw new Error(`Unknown tool: ${toolName}`);
9095
+ }
9096
+ }
9097
+
9098
+ // src/utils/toolsAdapter.ts
9099
+ var NoOpStorage = class extends BaseStorage {
9100
+ async upload(input, destination, options) {
9101
+ return `/tmp/${destination}`;
9102
+ }
9103
+ async download(path13) {
9104
+ throw new Error("Download not supported in CLI");
9105
+ }
9106
+ async delete(path13) {
9107
+ }
9108
+ async getSignedUrl(path13) {
9109
+ return `/tmp/${path13}`;
9110
+ }
9111
+ getPublicUrl(path13) {
9112
+ return `/tmp/${path13}`;
9113
+ }
9114
+ async getPreview(path13) {
9115
+ return `/tmp/${path13}`;
9116
+ }
9117
+ async getMetadata(path13) {
9118
+ return { size: 0, contentType: "application/octet-stream" };
9119
+ }
9120
+ };
9121
+ var CliLogger = class extends Logger {
9122
+ constructor() {
9123
+ super({ logInJson: false });
9124
+ }
9125
+ // Override all logging methods to be silent
9126
+ info(...args) {
9127
+ }
9128
+ warn(...args) {
9129
+ }
9130
+ error(...args) {
9131
+ }
9132
+ debug(...args) {
9133
+ }
9134
+ log(...args) {
9135
+ }
9136
+ };
9137
+ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
9138
+ const originalFn = tool.toolFn;
9139
+ const toolName = tool.toolSchema.name;
9140
+ return {
9141
+ ...tool,
9142
+ toolFn: async (args) => {
9143
+ if (!permissionManager.needsPermission(toolName)) {
9144
+ const result2 = await executeTool(toolName, args, apiClient, originalFn);
9145
+ agentContext.observationQueue.push({ toolName, result: result2 });
9146
+ return result2;
9147
+ }
9148
+ let preview;
9149
+ if (toolName === "edit_file" && args?.path && args?.content) {
9150
+ try {
9151
+ preview = await generateFileDiffPreview({
9152
+ path: args.path,
9153
+ content: args.content
9154
+ });
9155
+ } catch (error) {
9156
+ preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9157
+ }
9158
+ } else if (toolName === "create_file" && args?.path && args?.content) {
9159
+ try {
9160
+ preview = await generateFileDiffPreview({
9161
+ path: args.path,
9162
+ content: args.content
9163
+ });
9164
+ } catch (error) {
9165
+ preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9166
+ }
9167
+ } else if (toolName === "delete_file" && args?.path) {
9168
+ try {
9169
+ preview = await generateFileDeletePreview({
9170
+ path: args.path
9171
+ });
9172
+ } catch (error) {
9173
+ preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9174
+ }
9175
+ }
9176
+ const response = await showPermissionPrompt(toolName, args, preview);
9177
+ if (response.action === "deny") {
9178
+ throw new PermissionDeniedError(toolName, args);
9179
+ }
9180
+ if (response.action === "allow-always") {
9181
+ const canTrust = permissionManager.trustTool(toolName);
9182
+ if (canTrust) {
9183
+ const projectDir = configStore.getProjectConfigDir();
9184
+ if (projectDir) {
9185
+ try {
9186
+ await configStore.initProjectConfig();
9187
+ const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
9188
+ const updatedLocal = {
9189
+ ...existingLocal,
9190
+ trustedTools: [...existingLocal.trustedTools || [], toolName]
9191
+ };
9192
+ await configStore.saveProjectLocalConfig(updatedLocal);
9193
+ } catch (error) {
9194
+ await configStore.trustTool(toolName);
9195
+ }
9196
+ } else {
9197
+ await configStore.trustTool(toolName);
9198
+ }
9199
+ }
9200
+ }
9201
+ const result = await executeTool(toolName, args, apiClient, originalFn);
9202
+ agentContext.observationQueue.push({ toolName, result });
9203
+ return result;
9204
+ }
9205
+ };
9206
+ }
9207
+ function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
9208
+ const logger2 = new CliLogger();
9209
+ const storage = new NoOpStorage();
9210
+ const user = {
9211
+ _id: userId,
9212
+ email: "cli-user@bike4mind.local",
9213
+ firstName: "CLI",
9214
+ lastName: "User"
9215
+ };
9216
+ const statusUpdate = async (q, status) => {
9217
+ };
8981
9218
  const onStart = async (toolName, data) => {
8982
9219
  };
8983
9220
  const onFinish = async (toolName, data) => {
8984
9221
  };
8985
9222
  const mockAdminSettings = {
8986
9223
  findBySettingName: async (settingName) => {
8987
- if (settingName === "OpenWeatherKey") {
8988
- const key = toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY;
8989
- if (key) {
8990
- return { settingValue: key };
8991
- }
8992
- }
8993
- if (settingName === "SerperKey") {
8994
- const key = toolApiKeys?.serper || process.env.SERPER_API_KEY;
8995
- if (key) {
8996
- return { settingValue: key };
8997
- }
8998
- }
8999
9224
  return null;
9000
9225
  }
9001
9226
  };
@@ -9005,27 +9230,22 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
9005
9230
  adminSettings: mockAdminSettings
9006
9231
  }
9007
9232
  };
9008
- const hasSerperKey = !!(toolApiKeys?.serper || process.env.SERPER_API_KEY);
9009
- const hasOpenWeatherKey = !!(toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY);
9010
9233
  const toolConfig = {
9011
- // Enable these tools for CLI (no external API keys needed)
9234
+ // Local-only tools (no external API keys needed)
9012
9235
  dice_roll: {},
9013
9236
  math_evaluate: {},
9014
9237
  current_datetime: {},
9015
9238
  prompt_enhancement: {},
9016
- // File operation tools (CLI-specific)
9239
+ // File operation tools (CLI-specific, local execution)
9017
9240
  file_read: {},
9018
9241
  create_file: {},
9019
9242
  glob_files: {},
9020
9243
  grep_search: {},
9021
9244
  delete_file: {},
9022
- // Tools requiring API keys (only include if API key is available)
9023
- ...hasSerperKey && { web_search: {} },
9024
- // Requires SERPER_API_KEY
9025
- ...hasOpenWeatherKey && { weather_info: {} },
9026
- // Requires OPENWEATHER_API_KEY
9027
- ...hasSerperKey && { deep_research: {} }
9028
- // Requires SERPER_API_KEY
9245
+ // Server-side tools (executed via /api/ai/v1/tools Lambda)
9246
+ // Routed by ToolRouter to use B4M company API keys
9247
+ weather_info: {},
9248
+ web_search: {}
9029
9249
  // Disable web-only tools
9030
9250
  // blog_publish: undefined,
9031
9251
  // blog_edit: undefined,
@@ -9035,6 +9255,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
9035
9255
  // mermaid_chart: undefined,
9036
9256
  // recharts: undefined,
9037
9257
  // edit_file: undefined,
9258
+ // deep_research: undefined,
9038
9259
  };
9039
9260
  const toolsMap = generateTools(
9040
9261
  userId,
@@ -9052,7 +9273,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
9052
9273
  model
9053
9274
  );
9054
9275
  const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
9055
- ([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
9276
+ ([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
9056
9277
  );
9057
9278
  return { tools: tools2, agentContext };
9058
9279
  }
@@ -9123,384 +9344,129 @@ var PermissionManager = class {
9123
9344
  this.deniedTools = /* @__PURE__ */ new Set();
9124
9345
  this.trustedTools = new Set(trustedTools);
9125
9346
  this.customCategories = new Map(Object.entries(customCategories || {}));
9126
- this.deniedTools = new Set(deniedTools || []);
9127
- }
9128
- /**
9129
- * Check if a tool needs permission before execution
9130
- *
9131
- * A tool needs permission if:
9132
- * 1. It's in the denied tools list (from project config), OR
9133
- * 2. It's not in the auto_approve category, AND
9134
- * 3. It's not in the trusted tools set
9135
- *
9136
- * Note: prompt_always tools CANNOT be trusted, so they always need permission
9137
- * Note: denied tools from project config ALWAYS need permission (cannot be overridden locally)
9138
- */
9139
- needsPermission(toolName) {
9140
- const categoryMap = Object.fromEntries(this.customCategories);
9141
- const category = getToolCategory(toolName, categoryMap);
9142
- if (this.deniedTools.has(toolName)) {
9143
- return true;
9144
- }
9145
- if (category === "auto_approve") {
9146
- return false;
9147
- }
9148
- if (category === "prompt_always") {
9149
- return true;
9150
- }
9151
- return !this.trustedTools.has(toolName);
9152
- }
9153
- /**
9154
- * Add a tool to the trusted list
9155
- * Returns false if the tool cannot be trusted (prompt_always category or denied by project)
9156
- */
9157
- trustTool(toolName) {
9158
- const categoryMap = Object.fromEntries(this.customCategories);
9159
- if (this.deniedTools.has(toolName)) {
9160
- return false;
9161
- }
9162
- if (!canTrustTool(toolName, categoryMap)) {
9163
- return false;
9164
- }
9165
- this.trustedTools.add(toolName);
9166
- return true;
9167
- }
9168
- /**
9169
- * Remove a tool from the trusted list
9170
- */
9171
- untrustTool(toolName) {
9172
- this.trustedTools.delete(toolName);
9173
- }
9174
- /**
9175
- * Get list of all trusted tools
9176
- */
9177
- getTrustedTools() {
9178
- return Array.from(this.trustedTools).sort();
9179
- }
9180
- /**
9181
- * Check if a tool is currently trusted
9182
- */
9183
- isTrusted(toolName) {
9184
- return this.trustedTools.has(toolName);
9185
- }
9186
- /**
9187
- * Get the category for a tool
9188
- */
9189
- getCategory(toolName) {
9190
- const categoryMap = Object.fromEntries(this.customCategories);
9191
- return getToolCategory(toolName, categoryMap);
9192
- }
9193
- /**
9194
- * Check if a tool can be trusted (not in prompt_always category or denied by project)
9195
- */
9196
- canBeTrusted(toolName) {
9197
- const categoryMap = Object.fromEntries(this.customCategories);
9198
- if (this.deniedTools.has(toolName)) {
9199
- return false;
9200
- }
9201
- return canTrustTool(toolName, categoryMap);
9202
- }
9203
- /**
9204
- * Check if a tool is denied by project config
9205
- */
9206
- isDenied(toolName) {
9207
- return this.deniedTools.has(toolName);
9208
- }
9209
- /**
9210
- * Get list of denied tools
9211
- */
9212
- getDeniedTools() {
9213
- return Array.from(this.deniedTools).sort();
9214
- }
9215
- /**
9216
- * Clear all trusted tools
9217
- */
9218
- clearTrustedTools() {
9219
- this.trustedTools.clear();
9220
- }
9221
- /**
9222
- * Get statistics about permissions
9223
- */
9224
- getStats() {
9225
- return {
9226
- trustedCount: this.trustedTools.size,
9227
- autoApproveCount: 0,
9228
- // TODO: Would need full tool list
9229
- promptAlwaysCount: 0,
9230
- promptDefaultCount: 0
9231
- };
9232
- }
9233
- };
9234
-
9235
- // src/utils/apiUrl.ts
9236
- var BIKE4MIND_URL = "https://app.bike4mind.com";
9237
- function getApiUrl(configApiConfig) {
9238
- if (configApiConfig?.customUrl) {
9239
- return configApiConfig.customUrl;
9240
- }
9241
- return BIKE4MIND_URL;
9242
- }
9243
- function getEnvironmentName(configApiConfig) {
9244
- if (configApiConfig?.customUrl) {
9245
- return "Self-Hosted";
9246
- }
9247
- return "Bike4Mind";
9248
- }
9249
-
9250
- // src/utils/Logger.ts
9251
- import fs10 from "fs/promises";
9252
- import path11 from "path";
9253
- import os from "os";
9254
- var Logger2 = class _Logger {
9255
- constructor() {
9256
- this.logFilePath = null;
9257
- this.sessionId = null;
9258
- this.fileLoggingEnabled = true;
9259
- this.consoleVerbose = false;
9260
- }
9261
- static {
9262
- this.instance = null;
9263
- }
9264
- static getInstance() {
9265
- if (!_Logger.instance) {
9266
- _Logger.instance = new _Logger();
9267
- }
9268
- return _Logger.instance;
9269
- }
9270
- /**
9271
- * Initialize the logger with a session ID
9272
- */
9273
- async initialize(sessionId) {
9274
- this.sessionId = sessionId;
9275
- const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9276
- await fs10.mkdir(debugDir, { recursive: true });
9277
- this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
9278
- await this.writeToFile("INFO", "=== CLI SESSION START ===");
9279
- }
9280
- /**
9281
- * Set whether verbose console logging is enabled
9282
- */
9283
- setVerbose(enabled) {
9284
- this.consoleVerbose = enabled;
9285
- }
9286
- /**
9287
- * Set whether file logging is enabled
9288
- */
9289
- setFileLoggingEnabled(enabled) {
9290
- this.fileLoggingEnabled = enabled;
9291
- }
9292
- /**
9293
- * DEBUG level - verbose-only console, always file
9294
- */
9295
- debug(message) {
9296
- this.writeToFile("DEBUG", message).catch(() => {
9297
- });
9298
- if (this.consoleVerbose) {
9299
- console.log(message);
9300
- }
9347
+ this.deniedTools = new Set(deniedTools || []);
9301
9348
  }
9302
9349
  /**
9303
- * INFO level - always shown to user
9350
+ * Check if a tool needs permission before execution
9351
+ *
9352
+ * A tool needs permission if:
9353
+ * 1. It's in the denied tools list (from project config), OR
9354
+ * 2. It's not in the auto_approve category, AND
9355
+ * 3. It's not in the trusted tools set
9356
+ *
9357
+ * Note: prompt_always tools CANNOT be trusted, so they always need permission
9358
+ * Note: denied tools from project config ALWAYS need permission (cannot be overridden locally)
9304
9359
  */
9305
- info(message) {
9306
- this.writeToFile("INFO", message).catch(() => {
9307
- });
9308
- console.log(message);
9360
+ needsPermission(toolName) {
9361
+ const categoryMap = Object.fromEntries(this.customCategories);
9362
+ const category = getToolCategory(toolName, categoryMap);
9363
+ if (this.deniedTools.has(toolName)) {
9364
+ return true;
9365
+ }
9366
+ if (category === "auto_approve") {
9367
+ return false;
9368
+ }
9369
+ if (category === "prompt_always") {
9370
+ return true;
9371
+ }
9372
+ return !this.trustedTools.has(toolName);
9309
9373
  }
9310
9374
  /**
9311
- * WARN level - always shown to user
9375
+ * Add a tool to the trusted list
9376
+ * Returns false if the tool cannot be trusted (prompt_always category or denied by project)
9312
9377
  */
9313
- warn(message) {
9314
- this.writeToFile("WARN", message).catch(() => {
9315
- });
9316
- console.warn(message);
9378
+ trustTool(toolName) {
9379
+ const categoryMap = Object.fromEntries(this.customCategories);
9380
+ if (this.deniedTools.has(toolName)) {
9381
+ return false;
9382
+ }
9383
+ if (!canTrustTool(toolName, categoryMap)) {
9384
+ return false;
9385
+ }
9386
+ this.trustedTools.add(toolName);
9387
+ return true;
9317
9388
  }
9318
9389
  /**
9319
- * ERROR level - always shown to user
9390
+ * Remove a tool from the trusted list
9320
9391
  */
9321
- error(message, err) {
9322
- this.writeToFile("ERROR", message).catch(() => {
9323
- });
9324
- if (err) {
9325
- this.logErrorDetailsToFile(err).catch(() => {
9326
- });
9327
- }
9328
- console.error(message);
9392
+ untrustTool(toolName) {
9393
+ this.trustedTools.delete(toolName);
9329
9394
  }
9330
9395
  /**
9331
- * Write log entry to file
9396
+ * Get list of all trusted tools
9332
9397
  */
9333
- async writeToFile(level, message) {
9334
- if (!this.fileLoggingEnabled || !this.logFilePath) {
9335
- return;
9336
- }
9337
- try {
9338
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
9339
- const logEntry = `[${timestamp}] [${level}] ${message}
9340
- `;
9341
- await fs10.appendFile(this.logFilePath, logEntry, "utf-8");
9342
- } catch (error) {
9343
- console.error("File logging failed:", error);
9344
- }
9398
+ getTrustedTools() {
9399
+ return Array.from(this.trustedTools).sort();
9345
9400
  }
9346
9401
  /**
9347
- * Log error details to file
9402
+ * Check if a tool is currently trusted
9348
9403
  */
9349
- async logErrorDetailsToFile(err) {
9350
- if (!this.fileLoggingEnabled || !this.logFilePath) {
9351
- return;
9352
- }
9353
- try {
9354
- if (err && typeof err === "object" && "response" in err && err.response) {
9355
- const response = err.response;
9356
- const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
9357
- await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
9358
- await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
9359
- await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
9360
- if (response.data) {
9361
- const errorText = this.extractErrorMessage(response.data);
9362
- if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
9363
- await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
9364
- const parsedError = this.parseHtmlError(errorText);
9365
- if (parsedError) {
9366
- await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
9367
- }
9368
- await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
9369
- } else {
9370
- const preview = this.truncate(errorText, 500);
9371
- await this.writeToFile("ERROR", ` Response: ${preview}`);
9372
- }
9373
- } else {
9374
- await this.writeToFile("ERROR", ` Response: (no data)`);
9375
- }
9376
- }
9377
- if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
9378
- const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
9379
- await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
9380
- } else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
9381
- await this.writeToFile("ERROR", ` Message: ${err.message}`);
9382
- }
9383
- } catch (error) {
9384
- }
9404
+ isTrusted(toolName) {
9405
+ return this.trustedTools.has(toolName);
9385
9406
  }
9386
9407
  /**
9387
- * Safely stringify object, handling circular references
9408
+ * Get the category for a tool
9388
9409
  */
9389
- safeStringify(obj) {
9390
- try {
9391
- return JSON.stringify(obj);
9392
- } catch (error) {
9393
- if (error instanceof Error && error.message.includes("circular")) {
9394
- try {
9395
- const seen = /* @__PURE__ */ new WeakSet();
9396
- return JSON.stringify(obj, (key, value) => {
9397
- if (typeof value === "object" && value !== null) {
9398
- if (seen.has(value)) {
9399
- return "[Circular]";
9400
- }
9401
- seen.add(value);
9402
- }
9403
- return value;
9404
- });
9405
- } catch {
9406
- return "[Unable to stringify]";
9407
- }
9408
- }
9409
- return "[Stringify error]";
9410
- }
9410
+ getCategory(toolName) {
9411
+ const categoryMap = Object.fromEntries(this.customCategories);
9412
+ return getToolCategory(toolName, categoryMap);
9411
9413
  }
9412
9414
  /**
9413
- * Extract readable text from error response data
9415
+ * Check if a tool can be trusted (not in prompt_always category or denied by project)
9414
9416
  */
9415
- extractErrorMessage(data) {
9416
- if (Buffer.isBuffer(data)) {
9417
- return data.toString("utf-8");
9418
- }
9419
- if (typeof data === "string") {
9420
- return data;
9421
- }
9422
- if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
9423
- const chunks = [];
9424
- for (const chunk of data._readableState.buffer) {
9425
- if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
9426
- chunks.push(Buffer.from(chunk.data));
9427
- } else if (Buffer.isBuffer(chunk)) {
9428
- chunks.push(chunk);
9429
- } else if (chunk && typeof chunk === "object" && "data" in chunk) {
9430
- if (Buffer.isBuffer(chunk.data)) {
9431
- chunks.push(chunk.data);
9432
- } else if (Array.isArray(chunk.data)) {
9433
- chunks.push(Buffer.from(chunk.data));
9434
- }
9435
- }
9436
- }
9437
- if (chunks.length > 0) {
9438
- return Buffer.concat(chunks).toString("utf-8");
9439
- }
9417
+ canBeTrusted(toolName) {
9418
+ const categoryMap = Object.fromEntries(this.customCategories);
9419
+ if (this.deniedTools.has(toolName)) {
9420
+ return false;
9440
9421
  }
9441
- return this.safeStringify(data);
9422
+ return canTrustTool(toolName, categoryMap);
9442
9423
  }
9443
9424
  /**
9444
- * Parse HTML error page to extract error message
9425
+ * Check if a tool is denied by project config
9445
9426
  */
9446
- parseHtmlError(html) {
9447
- const titleMatch = html.match(/<title>(.*?)<\/title>/i);
9448
- const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
9449
- const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
9450
- if (titleMatch && titleMatch[1] !== "Error") {
9451
- return titleMatch[1].trim();
9452
- }
9453
- if (h1Match) {
9454
- return h1Match[1].trim();
9455
- }
9456
- if (bodyMatch) {
9457
- const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
9458
- return text.substring(0, 200);
9459
- }
9460
- return null;
9427
+ isDenied(toolName) {
9428
+ return this.deniedTools.has(toolName);
9461
9429
  }
9462
9430
  /**
9463
- * Truncate string to max length with ellipsis
9431
+ * Get list of denied tools
9464
9432
  */
9465
- truncate(str, maxLength) {
9466
- if (str.length <= maxLength) {
9467
- return str;
9468
- }
9469
- return str.substring(0, maxLength) + "... [truncated]";
9433
+ getDeniedTools() {
9434
+ return Array.from(this.deniedTools).sort();
9470
9435
  }
9471
9436
  /**
9472
- * Format bytes to human-readable size
9437
+ * Clear all trusted tools
9473
9438
  */
9474
- formatBytes(bytes) {
9475
- if (bytes === 0) return "0 B";
9476
- const k = 1024;
9477
- const sizes = ["B", "KB", "MB", "GB"];
9478
- const i = Math.floor(Math.log(bytes) / Math.log(k));
9479
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
9439
+ clearTrustedTools() {
9440
+ this.trustedTools.clear();
9480
9441
  }
9481
9442
  /**
9482
- * Clean up old debug logs (older than 30 days)
9443
+ * Get statistics about permissions
9483
9444
  */
9484
- async cleanupOldLogs() {
9485
- if (!this.fileLoggingEnabled) return;
9486
- try {
9487
- const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9488
- const files = await fs10.readdir(debugDir);
9489
- const now = Date.now();
9490
- const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
9491
- for (const file of files) {
9492
- const filePath = path11.join(debugDir, file);
9493
- const stats = await fs10.stat(filePath);
9494
- if (stats.mtime.getTime() < thirtyDaysAgo) {
9495
- await fs10.unlink(filePath);
9496
- }
9497
- }
9498
- } catch (error) {
9499
- console.error("Failed to cleanup old logs:", error);
9500
- }
9445
+ getStats() {
9446
+ return {
9447
+ trustedCount: this.trustedTools.size,
9448
+ autoApproveCount: 0,
9449
+ // TODO: Would need full tool list
9450
+ promptAlwaysCount: 0,
9451
+ promptDefaultCount: 0
9452
+ };
9501
9453
  }
9502
9454
  };
9503
- var logger = Logger2.getInstance();
9455
+
9456
+ // src/utils/apiUrl.ts
9457
+ var BIKE4MIND_URL = "https://app.bike4mind.com";
9458
+ function getApiUrl(configApiConfig) {
9459
+ if (configApiConfig?.customUrl) {
9460
+ return configApiConfig.customUrl;
9461
+ }
9462
+ return BIKE4MIND_URL;
9463
+ }
9464
+ function getEnvironmentName(configApiConfig) {
9465
+ if (configApiConfig?.customUrl) {
9466
+ return "Self-Hosted";
9467
+ }
9468
+ return "Bike4Mind";
9469
+ }
9504
9470
 
9505
9471
  // ../../b4m-core/packages/mcp/dist/src/client.js
9506
9472
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
@@ -10305,7 +10271,10 @@ var ApiClient = class {
10305
10271
  * Make a POST request
10306
10272
  */
10307
10273
  async post(url, data, config) {
10274
+ logger.debug(`[ApiClient] POST ${this.client.defaults.baseURL}${url}`);
10275
+ logger.debug(`[ApiClient] Request body: ${JSON.stringify(data)}`);
10308
10276
  const response = await this.client.post(url, data, config);
10277
+ logger.debug(`[ApiClient] Response status: ${response.status}`);
10309
10278
  return response.data;
10310
10279
  }
10311
10280
  /**
@@ -10356,7 +10325,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
10356
10325
  // package.json
10357
10326
  var package_default = {
10358
10327
  name: "@bike4mind/cli",
10359
- version: "0.2.7-basic-cli-shortcuts.17131+b79f34724",
10328
+ version: "0.2.7-feat-cli-tool-lambda-v2.17129+8e46f37a5",
10360
10329
  type: "module",
10361
10330
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
10362
10331
  license: "UNLICENSED",
@@ -10459,10 +10428,10 @@ var package_default = {
10459
10428
  },
10460
10429
  devDependencies: {
10461
10430
  "@bike4mind/agents": "0.1.0",
10462
- "@bike4mind/common": "2.38.1-basic-cli-shortcuts.17131+b79f34724",
10463
- "@bike4mind/mcp": "1.20.1-basic-cli-shortcuts.17131+b79f34724",
10464
- "@bike4mind/services": "2.33.1-basic-cli-shortcuts.17131+b79f34724",
10465
- "@bike4mind/utils": "2.1.2-basic-cli-shortcuts.17131+b79f34724",
10431
+ "@bike4mind/common": "2.38.1-feat-cli-tool-lambda-v2.17129+8e46f37a5",
10432
+ "@bike4mind/mcp": "1.20.1-feat-cli-tool-lambda-v2.17129+8e46f37a5",
10433
+ "@bike4mind/services": "2.33.1-feat-cli-tool-lambda-v2.17129+8e46f37a5",
10434
+ "@bike4mind/utils": "2.1.2-feat-cli-tool-lambda-v2.17129+8e46f37a5",
10466
10435
  "@types/better-sqlite3": "^7.6.12",
10467
10436
  "@types/diff": "^5.0.9",
10468
10437
  "@types/jsonwebtoken": "^9.0.4",
@@ -10475,7 +10444,7 @@ var package_default = {
10475
10444
  typescript: "^5.9.3",
10476
10445
  vitest: "^3.2.4"
10477
10446
  },
10478
- gitHead: "b79f34724ba9a60aae19010065d78ac75c2f8396"
10447
+ gitHead: "8e46f37a5b295f66cc4da97b2134509dc23cd237"
10479
10448
  };
10480
10449
 
10481
10450
  // src/config/constants.ts
@@ -10497,7 +10466,7 @@ var usageCache = null;
10497
10466
  function CliApp() {
10498
10467
  const { exit } = useApp();
10499
10468
  const imageRenderer = new ImageRenderer();
10500
- const [state, setState] = useState5({
10469
+ const [state, setState] = useState4({
10501
10470
  session: null,
10502
10471
  sessionStore: new SessionStore(),
10503
10472
  configStore: new ConfigStore(),
@@ -10512,9 +10481,9 @@ function CliApp() {
10512
10481
  permissionPrompt: null,
10513
10482
  trustLocationSelector: null
10514
10483
  });
10515
- const [isInitialized, setIsInitialized] = useState5(false);
10516
- const [initError, setInitError] = useState5(null);
10517
- const [commandHistory, setCommandHistory] = useState5([]);
10484
+ const [isInitialized, setIsInitialized] = useState4(false);
10485
+ const [initError, setInitError] = useState4(null);
10486
+ const [commandHistory, setCommandHistory] = useState4([]);
10518
10487
  const imageStoreInitPromise = useRef(null);
10519
10488
  const setStoreSession = useCliStore((state2) => state2.setSession);
10520
10489
  const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
@@ -10555,7 +10524,7 @@ function CliApp() {
10555
10524
  process.exit(0);
10556
10525
  }, 100);
10557
10526
  }, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
10558
- useInput4((input, key) => {
10527
+ useInput3((input, key) => {
10559
10528
  if (key.ctrl && input === "c") {
10560
10529
  const now = Date.now();
10561
10530
  if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
@@ -10699,10 +10668,6 @@ function CliApp() {
10699
10668
  setPermissionPrompt(prompt);
10700
10669
  });
10701
10670
  };
10702
- console.log("\u{1F511} Tool API Keys:", {
10703
- openweather: config.toolApiKeys?.openweather ? "\u2713 configured" : "\u2717 missing",
10704
- serper: config.toolApiKeys?.serper ? "\u2713 configured" : "\u2717 missing"
10705
- });
10706
10671
  const agentContext = {
10707
10672
  currentAgent: null,
10708
10673
  observationQueue: []
@@ -10715,7 +10680,7 @@ function CliApp() {
10715
10680
  promptFn,
10716
10681
  agentContext,
10717
10682
  state.configStore,
10718
- config.toolApiKeys
10683
+ apiClient
10719
10684
  );
10720
10685
  console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
10721
10686
  const mcpManager = new McpManager(config);
@@ -11345,12 +11310,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
11345
11310
  setState((prev) => ({ ...prev, config: updatedConfig }));
11346
11311
  };
11347
11312
  if (initError) {
11348
- return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React14.createElement(Text13, null, initError), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
11313
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React13.createElement(Text12, null, initError), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
11349
11314
  }
11350
11315
  if (state.trustLocationSelector) {
11351
11316
  const projectDir = state.configStore.getProjectConfigDir();
11352
11317
  const inProject = projectDir !== null;
11353
- return /* @__PURE__ */ React14.createElement(
11318
+ return /* @__PURE__ */ React13.createElement(
11354
11319
  TrustLocationSelector,
11355
11320
  {
11356
11321
  inProject,
@@ -11369,7 +11334,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
11369
11334
  }
11370
11335
  if (state.showLoginFlow) {
11371
11336
  const loginApiUrl = getApiUrl(state.config?.apiConfig);
11372
- return /* @__PURE__ */ React14.createElement(
11337
+ return /* @__PURE__ */ React13.createElement(
11373
11338
  LoginFlow,
11374
11339
  {
11375
11340
  apiUrl: loginApiUrl,
@@ -11392,9 +11357,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
11392
11357
  );
11393
11358
  }
11394
11359
  if (!isInitialized) {
11395
- return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "\u{1F680} Initializing..."));
11360
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "\u{1F680} Initializing..."));
11396
11361
  }
11397
- return /* @__PURE__ */ React14.createElement(
11362
+ return /* @__PURE__ */ React13.createElement(
11398
11363
  App,
11399
11364
  {
11400
11365
  onMessage: handleMessage,
@@ -11429,6 +11394,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
11429
11394
  if (isDevMode) {
11430
11395
  console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
11431
11396
  }
11432
- render(/* @__PURE__ */ React14.createElement(CliApp, null), {
11397
+ render(/* @__PURE__ */ React13.createElement(CliApp, null), {
11433
11398
  exitOnCtrlC: false
11434
11399
  });