@bike4mind/cli 0.2.3-feat-cli-clear-command.17058 → 0.2.3-feat-cli-project-scoped-config.17063
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +838 -166
- 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
|
|
83
|
-
import { render, Box as
|
|
82
|
+
import React13, { useState as useState4, useEffect as useEffect3, useCallback } 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
|
|
88
|
-
import { Box as
|
|
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";
|
|
@@ -352,7 +352,7 @@ var COMMANDS = [
|
|
|
352
352
|
},
|
|
353
353
|
{
|
|
354
354
|
name: "config",
|
|
355
|
-
description: "
|
|
355
|
+
description: "Open interactive configuration editor"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
name: "set-api",
|
|
@@ -384,11 +384,6 @@ var COMMANDS = [
|
|
|
384
384
|
{
|
|
385
385
|
name: "usage",
|
|
386
386
|
description: "Show credit usage and balance"
|
|
387
|
-
},
|
|
388
|
-
{
|
|
389
|
-
name: "clear",
|
|
390
|
-
description: "Start a new session",
|
|
391
|
-
aliases: ["new"]
|
|
392
387
|
}
|
|
393
388
|
];
|
|
394
389
|
|
|
@@ -609,6 +604,9 @@ var useCliStore = create((set) => ({
|
|
|
609
604
|
// Permission prompt
|
|
610
605
|
permissionPrompt: null,
|
|
611
606
|
setPermissionPrompt: (prompt) => set({ permissionPrompt: prompt }),
|
|
607
|
+
// Config editor
|
|
608
|
+
showConfigEditor: false,
|
|
609
|
+
setShowConfigEditor: (show) => set({ showConfigEditor: show }),
|
|
612
610
|
// Exit handling
|
|
613
611
|
exitRequested: false,
|
|
614
612
|
setExitRequested: (requested) => set({ exitRequested: requested })
|
|
@@ -663,6 +661,211 @@ function PermissionPrompt({
|
|
|
663
661
|
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) })));
|
|
664
662
|
}
|
|
665
663
|
|
|
664
|
+
// src/components/ConfigEditor.tsx
|
|
665
|
+
import React8, { useState as useState2, useMemo as useMemo2 } from "react";
|
|
666
|
+
import { Box as Box8, Text as Text7, useInput as useInput2 } from "ink";
|
|
667
|
+
var MAX_ITERATIONS_OPTIONS = [
|
|
668
|
+
{ label: "10", value: 10 },
|
|
669
|
+
{ label: "20", value: 20 },
|
|
670
|
+
{ label: "30", value: 30 },
|
|
671
|
+
{ label: "40", value: 40 },
|
|
672
|
+
{ label: "50", value: 50 },
|
|
673
|
+
{ label: "Infinite", value: null }
|
|
674
|
+
];
|
|
675
|
+
var THEME_OPTIONS = [
|
|
676
|
+
{ label: "dark", value: "dark" },
|
|
677
|
+
{ label: "light", value: "light" }
|
|
678
|
+
];
|
|
679
|
+
var EXPORT_FORMAT_OPTIONS = [
|
|
680
|
+
{ label: "markdown", value: "markdown" },
|
|
681
|
+
{ label: "json", value: "json" }
|
|
682
|
+
];
|
|
683
|
+
var CONFIG_ITEMS = [
|
|
684
|
+
{
|
|
685
|
+
key: "maxIterations",
|
|
686
|
+
label: "Max Iterations",
|
|
687
|
+
type: "select",
|
|
688
|
+
options: MAX_ITERATIONS_OPTIONS,
|
|
689
|
+
getValue: (config) => config.preferences.maxIterations,
|
|
690
|
+
setValue: (config, value) => ({
|
|
691
|
+
...config,
|
|
692
|
+
preferences: {
|
|
693
|
+
...config.preferences,
|
|
694
|
+
maxIterations: value
|
|
695
|
+
}
|
|
696
|
+
})
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
key: "maxTokens",
|
|
700
|
+
label: "Max Tokens",
|
|
701
|
+
type: "number",
|
|
702
|
+
min: 256,
|
|
703
|
+
max: 128e3,
|
|
704
|
+
step: 256,
|
|
705
|
+
getValue: (config) => config.preferences.maxTokens,
|
|
706
|
+
setValue: (config, value) => ({
|
|
707
|
+
...config,
|
|
708
|
+
preferences: {
|
|
709
|
+
...config.preferences,
|
|
710
|
+
maxTokens: value
|
|
711
|
+
}
|
|
712
|
+
})
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
key: "temperature",
|
|
716
|
+
label: "Temperature",
|
|
717
|
+
type: "number",
|
|
718
|
+
min: 0,
|
|
719
|
+
max: 2,
|
|
720
|
+
step: 0.1,
|
|
721
|
+
getValue: (config) => config.preferences.temperature,
|
|
722
|
+
setValue: (config, value) => ({
|
|
723
|
+
...config,
|
|
724
|
+
preferences: {
|
|
725
|
+
...config.preferences,
|
|
726
|
+
temperature: value
|
|
727
|
+
}
|
|
728
|
+
})
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
key: "autoSave",
|
|
732
|
+
label: "Auto Save",
|
|
733
|
+
type: "boolean",
|
|
734
|
+
getValue: (config) => config.preferences.autoSave,
|
|
735
|
+
setValue: (config, value) => ({
|
|
736
|
+
...config,
|
|
737
|
+
preferences: {
|
|
738
|
+
...config.preferences,
|
|
739
|
+
autoSave: value
|
|
740
|
+
}
|
|
741
|
+
})
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
key: "theme",
|
|
745
|
+
label: "Theme",
|
|
746
|
+
type: "select",
|
|
747
|
+
options: THEME_OPTIONS,
|
|
748
|
+
getValue: (config) => config.preferences.theme,
|
|
749
|
+
setValue: (config, value) => ({
|
|
750
|
+
...config,
|
|
751
|
+
preferences: {
|
|
752
|
+
...config.preferences,
|
|
753
|
+
theme: value
|
|
754
|
+
}
|
|
755
|
+
})
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
key: "exportFormat",
|
|
759
|
+
label: "Export Format",
|
|
760
|
+
type: "select",
|
|
761
|
+
options: EXPORT_FORMAT_OPTIONS,
|
|
762
|
+
getValue: (config) => config.preferences.exportFormat,
|
|
763
|
+
setValue: (config, value) => ({
|
|
764
|
+
...config,
|
|
765
|
+
preferences: {
|
|
766
|
+
...config.preferences,
|
|
767
|
+
exportFormat: value
|
|
768
|
+
}
|
|
769
|
+
})
|
|
770
|
+
}
|
|
771
|
+
];
|
|
772
|
+
function ConfigEditor({ config, onSave, onClose }) {
|
|
773
|
+
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
774
|
+
const [editedConfig, setEditedConfig] = useState2(config);
|
|
775
|
+
const [saveError, setSaveError] = useState2(null);
|
|
776
|
+
const [isSaving, setIsSaving] = useState2(false);
|
|
777
|
+
const hasChanges = useMemo2(() => {
|
|
778
|
+
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences);
|
|
779
|
+
}, [config.preferences, editedConfig.preferences]);
|
|
780
|
+
const handleSaveAndClose = async () => {
|
|
781
|
+
if (hasChanges) {
|
|
782
|
+
try {
|
|
783
|
+
setSaveError(null);
|
|
784
|
+
setIsSaving(true);
|
|
785
|
+
await onSave(editedConfig);
|
|
786
|
+
} catch (error) {
|
|
787
|
+
setSaveError(error instanceof Error ? error.message : "Failed to save");
|
|
788
|
+
setIsSaving(false);
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
setIsSaving(false);
|
|
792
|
+
}
|
|
793
|
+
onClose();
|
|
794
|
+
};
|
|
795
|
+
useInput2((input, key) => {
|
|
796
|
+
const currentItem = CONFIG_ITEMS[selectedIndex];
|
|
797
|
+
if (key.upArrow) {
|
|
798
|
+
setSelectedIndex((prev) => prev > 0 ? prev - 1 : CONFIG_ITEMS.length - 1);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (key.downArrow) {
|
|
802
|
+
setSelectedIndex((prev) => prev < CONFIG_ITEMS.length - 1 ? prev + 1 : 0);
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if ((key.escape || input === "q") && !isSaving) {
|
|
806
|
+
handleSaveAndClose();
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
if (currentItem.type === "select" && currentItem.options) {
|
|
810
|
+
const currentValue = currentItem.getValue(editedConfig);
|
|
811
|
+
const currentIdx = currentItem.options.findIndex((opt) => opt.value === currentValue);
|
|
812
|
+
if (key.leftArrow || input === " ") {
|
|
813
|
+
let newIdx;
|
|
814
|
+
if (input === " ") {
|
|
815
|
+
newIdx = (currentIdx + 1) % currentItem.options.length;
|
|
816
|
+
} else {
|
|
817
|
+
newIdx = currentIdx > 0 ? currentIdx - 1 : currentItem.options.length - 1;
|
|
818
|
+
}
|
|
819
|
+
const newValue = currentItem.options[newIdx].value;
|
|
820
|
+
setEditedConfig(currentItem.setValue(editedConfig, newValue));
|
|
821
|
+
} else if (key.rightArrow) {
|
|
822
|
+
const newIdx = (currentIdx + 1) % currentItem.options.length;
|
|
823
|
+
const newValue = currentItem.options[newIdx].value;
|
|
824
|
+
setEditedConfig(currentItem.setValue(editedConfig, newValue));
|
|
825
|
+
}
|
|
826
|
+
} else if (currentItem.type === "boolean") {
|
|
827
|
+
if (input === " " || key.leftArrow || key.rightArrow) {
|
|
828
|
+
const currentValue = currentItem.getValue(editedConfig);
|
|
829
|
+
setEditedConfig(currentItem.setValue(editedConfig, !currentValue));
|
|
830
|
+
}
|
|
831
|
+
} else if (currentItem.type === "number") {
|
|
832
|
+
const currentValue = currentItem.getValue(editedConfig);
|
|
833
|
+
const step = currentItem.step || 1;
|
|
834
|
+
const min = currentItem.min ?? 0;
|
|
835
|
+
const max = currentItem.max ?? Infinity;
|
|
836
|
+
if (key.leftArrow || input === "-") {
|
|
837
|
+
const newValue = Math.max(min, currentValue - step);
|
|
838
|
+
const rounded = Math.round(newValue * 10) / 10;
|
|
839
|
+
setEditedConfig(currentItem.setValue(editedConfig, rounded));
|
|
840
|
+
} else if (key.rightArrow || input === "+" || input === "=") {
|
|
841
|
+
const newValue = Math.min(max, currentValue + step);
|
|
842
|
+
const rounded = Math.round(newValue * 10) / 10;
|
|
843
|
+
setEditedConfig(currentItem.setValue(editedConfig, rounded));
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
const formatValue = (item, value) => {
|
|
848
|
+
if (item.type === "boolean") {
|
|
849
|
+
return value ? "On" : "Off";
|
|
850
|
+
}
|
|
851
|
+
if (item.type === "select" && item.options) {
|
|
852
|
+
const option = item.options.find((opt) => opt.value === value);
|
|
853
|
+
return option?.label || String(value);
|
|
854
|
+
}
|
|
855
|
+
if (item.type === "number") {
|
|
856
|
+
return String(value);
|
|
857
|
+
}
|
|
858
|
+
return String(value);
|
|
859
|
+
};
|
|
860
|
+
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 }, CONFIG_ITEMS.map((item, index) => {
|
|
861
|
+
const isSelected = index === selectedIndex;
|
|
862
|
+
const value = item.getValue(editedConfig);
|
|
863
|
+
const displayValue = formatValue(item, value);
|
|
864
|
+
const hasOptions = item.type === "select" || item.type === "boolean" || item.type === "number";
|
|
865
|
+
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 }, " >")));
|
|
866
|
+
})), /* @__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")));
|
|
867
|
+
}
|
|
868
|
+
|
|
666
869
|
// src/utils/processFileReferences.ts
|
|
667
870
|
import * as fs2 from "fs";
|
|
668
871
|
import * as path2 from "path";
|
|
@@ -778,16 +981,25 @@ function hasFileReferences(message) {
|
|
|
778
981
|
}
|
|
779
982
|
|
|
780
983
|
// src/components/App.tsx
|
|
781
|
-
function App({
|
|
984
|
+
function App({
|
|
985
|
+
onMessage,
|
|
986
|
+
onCommand,
|
|
987
|
+
onPermissionResponse,
|
|
988
|
+
commandHistory = [],
|
|
989
|
+
config,
|
|
990
|
+
onSaveConfig
|
|
991
|
+
}) {
|
|
782
992
|
const messages = useCliStore((state) => state.session?.messages || []);
|
|
783
993
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
784
994
|
const currentModel = useCliStore((state) => state.session?.model || "claude-sonnet-4-5-20250929");
|
|
785
995
|
const totalTokens = useCliStore((state) => state.session?.metadata.totalTokens || 0);
|
|
786
996
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
787
997
|
const permissionPrompt = useCliStore((state) => state.permissionPrompt);
|
|
998
|
+
const showConfigEditor = useCliStore((state) => state.showConfigEditor);
|
|
999
|
+
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
788
1000
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
789
1001
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
790
|
-
const handleSubmit =
|
|
1002
|
+
const handleSubmit = React9.useCallback(
|
|
791
1003
|
async (input) => {
|
|
792
1004
|
const trimmed = input.trim();
|
|
793
1005
|
if (!trimmed) return;
|
|
@@ -816,7 +1028,7 @@ ${errorBlock}`;
|
|
|
816
1028
|
},
|
|
817
1029
|
[onMessage, onCommand, setIsThinking]
|
|
818
1030
|
);
|
|
819
|
-
return /* @__PURE__ */
|
|
1031
|
+
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(ConfigEditor, { config, onSave: onSaveConfig, onClose: () => setShowConfigEditor(false) })) : permissionPrompt ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
820
1032
|
PermissionPrompt,
|
|
821
1033
|
{
|
|
822
1034
|
toolName: permissionPrompt.toolName,
|
|
@@ -825,39 +1037,39 @@ ${errorBlock}`;
|
|
|
825
1037
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
826
1038
|
onResponse: onPermissionResponse
|
|
827
1039
|
}
|
|
828
|
-
)) : /* @__PURE__ */
|
|
1040
|
+
)) : /* @__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(InputPrompt, { onSubmit: handleSubmit, disabled: isThinking, history: commandHistory }))));
|
|
829
1041
|
}
|
|
830
1042
|
function MessageItem({ message }) {
|
|
831
|
-
return /* @__PURE__ */
|
|
1043
|
+
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) => {
|
|
832
1044
|
if (step.type === "thought") {
|
|
833
|
-
return /* @__PURE__ */
|
|
1045
|
+
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 ? "..." : ""}`));
|
|
834
1046
|
}
|
|
835
1047
|
if (step.type === "action") {
|
|
836
1048
|
const toolName = step.metadata?.toolName || "unknown";
|
|
837
1049
|
const toolInput = step.metadata?.toolInput;
|
|
838
1050
|
const observationStep = message.metadata.steps[idx + 1];
|
|
839
1051
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
840
|
-
return /* @__PURE__ */
|
|
1052
|
+
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 ? "..." : ""}`));
|
|
841
1053
|
}
|
|
842
1054
|
return null;
|
|
843
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1055
|
+
}).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`)));
|
|
844
1056
|
}
|
|
845
1057
|
|
|
846
1058
|
// src/components/MessageList.tsx
|
|
847
|
-
import
|
|
848
|
-
import { Box as
|
|
1059
|
+
import React10 from "react";
|
|
1060
|
+
import { Box as Box10, Text as Text9 } from "ink";
|
|
849
1061
|
function stripThinkingTags(content) {
|
|
850
1062
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
851
1063
|
}
|
|
852
|
-
var MessageList =
|
|
1064
|
+
var MessageList = React10.memo(
|
|
853
1065
|
function MessageList2({ messages }) {
|
|
854
1066
|
if (messages.length === 0) {
|
|
855
|
-
return /* @__PURE__ */
|
|
1067
|
+
return /* @__PURE__ */ React10.createElement(Box10, { paddingY: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
856
1068
|
}
|
|
857
|
-
return /* @__PURE__ */
|
|
1069
|
+
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) => {
|
|
858
1070
|
if (step.type === "thought") {
|
|
859
|
-
return /* @__PURE__ */
|
|
860
|
-
|
|
1071
|
+
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(
|
|
1072
|
+
Text9,
|
|
861
1073
|
{
|
|
862
1074
|
dimColor: true
|
|
863
1075
|
},
|
|
@@ -869,10 +1081,10 @@ var MessageList = React9.memo(
|
|
|
869
1081
|
const toolInput = step.metadata?.toolInput;
|
|
870
1082
|
const observationStep = message.metadata.steps[idx + 1];
|
|
871
1083
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
872
|
-
return /* @__PURE__ */
|
|
1084
|
+
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 ? "..." : ""}`));
|
|
873
1085
|
}
|
|
874
1086
|
return null;
|
|
875
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1087
|
+
}).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`)))));
|
|
876
1088
|
},
|
|
877
1089
|
(prevProps, nextProps) => {
|
|
878
1090
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -882,9 +1094,42 @@ var MessageList = React9.memo(
|
|
|
882
1094
|
}
|
|
883
1095
|
);
|
|
884
1096
|
|
|
1097
|
+
// src/components/TrustLocationSelector.tsx
|
|
1098
|
+
import React11 from "react";
|
|
1099
|
+
import { Box as Box11, Text as Text10 } from "ink";
|
|
1100
|
+
import SelectInput2 from "ink-select-input";
|
|
1101
|
+
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
1102
|
+
const items = [];
|
|
1103
|
+
if (inProject) {
|
|
1104
|
+
items.push({
|
|
1105
|
+
label: "Project settings (local) - Saved in .bike4mind/local.json (not committed)",
|
|
1106
|
+
value: "local"
|
|
1107
|
+
});
|
|
1108
|
+
items.push({
|
|
1109
|
+
label: "Project settings (team-wide) - Checked in at .bike4mind/config.json",
|
|
1110
|
+
value: "project"
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
items.push({
|
|
1114
|
+
label: "User settings - Saved at ~/.bike4mind/config.json",
|
|
1115
|
+
value: "global"
|
|
1116
|
+
});
|
|
1117
|
+
const handleSelect = (item) => {
|
|
1118
|
+
onSelect(item.value);
|
|
1119
|
+
};
|
|
1120
|
+
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(
|
|
1121
|
+
SelectInput2,
|
|
1122
|
+
{
|
|
1123
|
+
items,
|
|
1124
|
+
onSelect: handleSelect,
|
|
1125
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1126
|
+
}
|
|
1127
|
+
), /* @__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")));
|
|
1128
|
+
}
|
|
1129
|
+
|
|
885
1130
|
// src/components/LoginFlow.tsx
|
|
886
|
-
import
|
|
887
|
-
import { Box as
|
|
1131
|
+
import React12, { useState as useState3, useEffect as useEffect2 } from "react";
|
|
1132
|
+
import { Box as Box12, Text as Text11 } from "ink";
|
|
888
1133
|
import Spinner2 from "ink-spinner";
|
|
889
1134
|
import jwt from "jsonwebtoken";
|
|
890
1135
|
import open from "open";
|
|
@@ -1000,10 +1245,10 @@ var OAuthClient = class {
|
|
|
1000
1245
|
|
|
1001
1246
|
// src/components/LoginFlow.tsx
|
|
1002
1247
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
1003
|
-
const [status, setStatus] =
|
|
1004
|
-
const [deviceFlow, setDeviceFlow] =
|
|
1005
|
-
const [statusMessage, setStatusMessage] =
|
|
1006
|
-
const [error, setError] =
|
|
1248
|
+
const [status, setStatus] = useState3("initiating");
|
|
1249
|
+
const [deviceFlow, setDeviceFlow] = useState3(null);
|
|
1250
|
+
const [statusMessage, setStatusMessage] = useState3("Initiating device authorization...");
|
|
1251
|
+
const [error, setError] = useState3(null);
|
|
1007
1252
|
useEffect2(() => {
|
|
1008
1253
|
const runLoginFlow = async () => {
|
|
1009
1254
|
const oauth = new OAuthClient(apiUrl);
|
|
@@ -1047,15 +1292,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1047
1292
|
}
|
|
1048
1293
|
}, [deviceFlow, status]);
|
|
1049
1294
|
if (status === "initiating") {
|
|
1050
|
-
return /* @__PURE__ */
|
|
1295
|
+
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...")));
|
|
1051
1296
|
}
|
|
1052
1297
|
if (status === "error") {
|
|
1053
|
-
return /* @__PURE__ */
|
|
1298
|
+
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)));
|
|
1054
1299
|
}
|
|
1055
1300
|
if (status === "success") {
|
|
1056
|
-
return /* @__PURE__ */
|
|
1301
|
+
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.")));
|
|
1057
1302
|
}
|
|
1058
|
-
return /* @__PURE__ */
|
|
1303
|
+
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")));
|
|
1059
1304
|
}
|
|
1060
1305
|
|
|
1061
1306
|
// src/storage/SessionStore.ts
|
|
@@ -1177,7 +1422,7 @@ var SessionStore = class {
|
|
|
1177
1422
|
};
|
|
1178
1423
|
|
|
1179
1424
|
// src/storage/ConfigStore.ts
|
|
1180
|
-
import { promises as fs4 } from "fs";
|
|
1425
|
+
import { promises as fs4, existsSync as existsSync2 } from "fs";
|
|
1181
1426
|
import path4 from "path";
|
|
1182
1427
|
import { homedir as homedir2 } from "os";
|
|
1183
1428
|
import { v4 as uuidv4 } from "uuid";
|
|
@@ -1215,7 +1460,8 @@ var CliConfigSchema = z.object({
|
|
|
1215
1460
|
temperature: z.number(),
|
|
1216
1461
|
autoSave: z.boolean(),
|
|
1217
1462
|
theme: z.enum(["light", "dark"]),
|
|
1218
|
-
exportFormat: z.enum(["markdown", "json"])
|
|
1463
|
+
exportFormat: z.enum(["markdown", "json"]),
|
|
1464
|
+
maxIterations: z.number().nullable().default(10)
|
|
1219
1465
|
}),
|
|
1220
1466
|
tools: z.object({
|
|
1221
1467
|
enabled: z.array(z.string()),
|
|
@@ -1224,6 +1470,53 @@ var CliConfigSchema = z.object({
|
|
|
1224
1470
|
}),
|
|
1225
1471
|
trustedTools: z.array(z.string()).optional().default([])
|
|
1226
1472
|
});
|
|
1473
|
+
var ProjectConfigSchema = z.object({
|
|
1474
|
+
tools: z.object({
|
|
1475
|
+
enabled: z.array(z.string()).optional(),
|
|
1476
|
+
denied: z.array(z.string()).optional(),
|
|
1477
|
+
config: z.record(z.any()).optional()
|
|
1478
|
+
}).optional(),
|
|
1479
|
+
defaultModel: z.string().optional(),
|
|
1480
|
+
mcpServers: z.array(
|
|
1481
|
+
z.object({
|
|
1482
|
+
name: z.string(),
|
|
1483
|
+
command: z.string(),
|
|
1484
|
+
args: z.array(z.string()),
|
|
1485
|
+
env: z.record(z.string()),
|
|
1486
|
+
enabled: z.boolean()
|
|
1487
|
+
})
|
|
1488
|
+
).optional(),
|
|
1489
|
+
preferences: z.object({
|
|
1490
|
+
maxTokens: z.number().optional(),
|
|
1491
|
+
temperature: z.number().optional(),
|
|
1492
|
+
autoSave: z.boolean().optional(),
|
|
1493
|
+
theme: z.enum(["light", "dark"]).optional(),
|
|
1494
|
+
exportFormat: z.enum(["markdown", "json"]).optional()
|
|
1495
|
+
}).optional()
|
|
1496
|
+
});
|
|
1497
|
+
var ProjectLocalConfigSchema = z.object({
|
|
1498
|
+
trustedTools: z.array(z.string()).optional(),
|
|
1499
|
+
toolApiKeys: z.object({
|
|
1500
|
+
openweather: z.string().optional(),
|
|
1501
|
+
serper: z.string().optional()
|
|
1502
|
+
}).optional(),
|
|
1503
|
+
preferences: z.object({
|
|
1504
|
+
maxTokens: z.number().optional(),
|
|
1505
|
+
temperature: z.number().optional(),
|
|
1506
|
+
autoSave: z.boolean().optional(),
|
|
1507
|
+
theme: z.enum(["light", "dark"]).optional(),
|
|
1508
|
+
exportFormat: z.enum(["markdown", "json"]).optional()
|
|
1509
|
+
}).optional(),
|
|
1510
|
+
mcpServers: z.array(
|
|
1511
|
+
z.object({
|
|
1512
|
+
name: z.string(),
|
|
1513
|
+
command: z.string(),
|
|
1514
|
+
args: z.array(z.string()),
|
|
1515
|
+
env: z.record(z.string()),
|
|
1516
|
+
enabled: z.boolean()
|
|
1517
|
+
})
|
|
1518
|
+
).optional()
|
|
1519
|
+
});
|
|
1227
1520
|
var DEFAULT_CONFIG = {
|
|
1228
1521
|
version: "0.1.0",
|
|
1229
1522
|
userId: uuidv4(),
|
|
@@ -1238,7 +1531,8 @@ var DEFAULT_CONFIG = {
|
|
|
1238
1531
|
temperature: 0.7,
|
|
1239
1532
|
autoSave: true,
|
|
1240
1533
|
theme: "dark",
|
|
1241
|
-
exportFormat: "markdown"
|
|
1534
|
+
exportFormat: "markdown",
|
|
1535
|
+
maxIterations: 10
|
|
1242
1536
|
},
|
|
1243
1537
|
tools: {
|
|
1244
1538
|
enabled: [],
|
|
@@ -1249,9 +1543,123 @@ var DEFAULT_CONFIG = {
|
|
|
1249
1543
|
trustedTools: []
|
|
1250
1544
|
// No tools trusted by default
|
|
1251
1545
|
};
|
|
1546
|
+
function findProjectConfigDir(startDir = process.cwd()) {
|
|
1547
|
+
let currentDir = startDir;
|
|
1548
|
+
const { root } = path4.parse(currentDir);
|
|
1549
|
+
while (currentDir !== root) {
|
|
1550
|
+
const gitPath = path4.join(currentDir, ".git");
|
|
1551
|
+
try {
|
|
1552
|
+
if (existsSync2(gitPath)) {
|
|
1553
|
+
return currentDir;
|
|
1554
|
+
}
|
|
1555
|
+
} catch {
|
|
1556
|
+
}
|
|
1557
|
+
currentDir = path4.dirname(currentDir);
|
|
1558
|
+
}
|
|
1559
|
+
return process.cwd();
|
|
1560
|
+
}
|
|
1561
|
+
async function loadProjectConfig(projectDir) {
|
|
1562
|
+
const configPath = path4.join(projectDir, ".bike4mind", "config.json");
|
|
1563
|
+
try {
|
|
1564
|
+
const data = await fs4.readFile(configPath, "utf-8");
|
|
1565
|
+
const rawConfig = JSON.parse(data);
|
|
1566
|
+
const validated = ProjectConfigSchema.parse(rawConfig);
|
|
1567
|
+
return validated;
|
|
1568
|
+
} catch (error) {
|
|
1569
|
+
if (error.code === "ENOENT") {
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
if (error instanceof z.ZodError) {
|
|
1573
|
+
console.error("Project config validation error:", error.errors);
|
|
1574
|
+
return null;
|
|
1575
|
+
}
|
|
1576
|
+
console.error("Failed to load project config:", error);
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
async function loadProjectLocalConfig(projectDir) {
|
|
1581
|
+
const configPath = path4.join(projectDir, ".bike4mind", "local.json");
|
|
1582
|
+
try {
|
|
1583
|
+
const data = await fs4.readFile(configPath, "utf-8");
|
|
1584
|
+
const rawConfig = JSON.parse(data);
|
|
1585
|
+
const validated = ProjectLocalConfigSchema.parse(rawConfig);
|
|
1586
|
+
return validated;
|
|
1587
|
+
} catch (error) {
|
|
1588
|
+
if (error.code === "ENOENT") {
|
|
1589
|
+
return null;
|
|
1590
|
+
}
|
|
1591
|
+
if (error instanceof z.ZodError) {
|
|
1592
|
+
console.error("Project local config validation error:", error.errors);
|
|
1593
|
+
return null;
|
|
1594
|
+
}
|
|
1595
|
+
console.error("Failed to load project local config:", error);
|
|
1596
|
+
return null;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
function mergeMcpServers(...serverArrays) {
|
|
1600
|
+
const serverMap = /* @__PURE__ */ new Map();
|
|
1601
|
+
for (const servers of serverArrays) {
|
|
1602
|
+
if (servers) {
|
|
1603
|
+
for (const server of servers) {
|
|
1604
|
+
serverMap.set(server.name, server);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return Array.from(serverMap.values());
|
|
1609
|
+
}
|
|
1610
|
+
function mergeConfigs(global, project, local) {
|
|
1611
|
+
const merged = { ...global };
|
|
1612
|
+
if (project) {
|
|
1613
|
+
if (project.defaultModel) {
|
|
1614
|
+
merged.defaultModel = project.defaultModel;
|
|
1615
|
+
}
|
|
1616
|
+
if (project.preferences) {
|
|
1617
|
+
merged.preferences = {
|
|
1618
|
+
...merged.preferences,
|
|
1619
|
+
...project.preferences
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
if (project.tools) {
|
|
1623
|
+
merged.tools = {
|
|
1624
|
+
...merged.tools,
|
|
1625
|
+
enabled: project.tools.enabled || merged.tools.enabled,
|
|
1626
|
+
disabled: [...merged.tools.disabled, ...project.tools.denied || []],
|
|
1627
|
+
config: {
|
|
1628
|
+
...merged.tools.config,
|
|
1629
|
+
...project.tools.config
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
if (project.mcpServers) {
|
|
1634
|
+
merged.mcpServers = mergeMcpServers(merged.mcpServers, project.mcpServers);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
if (local) {
|
|
1638
|
+
if (local.trustedTools) {
|
|
1639
|
+
merged.trustedTools = [...merged.trustedTools || [], ...local.trustedTools];
|
|
1640
|
+
}
|
|
1641
|
+
if (local.toolApiKeys) {
|
|
1642
|
+
merged.toolApiKeys = {
|
|
1643
|
+
...merged.toolApiKeys,
|
|
1644
|
+
...local.toolApiKeys
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
if (local.preferences) {
|
|
1648
|
+
merged.preferences = {
|
|
1649
|
+
...merged.preferences,
|
|
1650
|
+
...local.preferences
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
if (local.mcpServers) {
|
|
1654
|
+
merged.mcpServers = mergeMcpServers(merged.mcpServers, local.mcpServers);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return merged;
|
|
1658
|
+
}
|
|
1252
1659
|
var ConfigStore = class {
|
|
1253
1660
|
constructor(configPath) {
|
|
1254
1661
|
this.config = null;
|
|
1662
|
+
this.projectConfigDir = null;
|
|
1255
1663
|
this.configPath = configPath || path4.join(homedir2(), ".bike4mind", "config.json");
|
|
1256
1664
|
}
|
|
1257
1665
|
/**
|
|
@@ -1268,51 +1676,76 @@ var ConfigStore = class {
|
|
|
1268
1676
|
}
|
|
1269
1677
|
/**
|
|
1270
1678
|
* Load configuration from disk with Zod validation
|
|
1679
|
+
* Merges global → project → local configs
|
|
1271
1680
|
*/
|
|
1272
1681
|
async load() {
|
|
1273
1682
|
if (this.config) {
|
|
1274
1683
|
return this.config;
|
|
1275
1684
|
}
|
|
1276
1685
|
try {
|
|
1686
|
+
let globalConfig;
|
|
1277
1687
|
try {
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1688
|
+
try {
|
|
1689
|
+
const stats = await fs4.stat(this.configPath);
|
|
1690
|
+
const mode = stats.mode & 511;
|
|
1691
|
+
if (mode !== 384) {
|
|
1692
|
+
console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
|
|
1693
|
+
await fs4.chmod(this.configPath, 384);
|
|
1694
|
+
}
|
|
1695
|
+
} catch (statError) {
|
|
1283
1696
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1697
|
+
const data = await fs4.readFile(this.configPath, "utf-8");
|
|
1698
|
+
const rawConfig = JSON.parse(data);
|
|
1699
|
+
if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
|
|
1700
|
+
const oldApiConfig = rawConfig.apiConfig;
|
|
1701
|
+
if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) {
|
|
1702
|
+
rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
|
|
1703
|
+
} else {
|
|
1704
|
+
rawConfig.apiConfig = {};
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
const validated = CliConfigSchema.parse(rawConfig);
|
|
1708
|
+
globalConfig = {
|
|
1709
|
+
...DEFAULT_CONFIG,
|
|
1710
|
+
...validated,
|
|
1711
|
+
auth: validated.auth,
|
|
1712
|
+
// Explicitly preserve auth field
|
|
1713
|
+
preferences: {
|
|
1714
|
+
...DEFAULT_CONFIG.preferences,
|
|
1715
|
+
...validated.preferences
|
|
1716
|
+
},
|
|
1717
|
+
tools: {
|
|
1718
|
+
...DEFAULT_CONFIG.tools,
|
|
1719
|
+
...validated.tools
|
|
1720
|
+
},
|
|
1721
|
+
toolApiKeys: {
|
|
1722
|
+
...DEFAULT_CONFIG.toolApiKeys,
|
|
1723
|
+
...validated.toolApiKeys || {}
|
|
1724
|
+
},
|
|
1725
|
+
trustedTools: validated.trustedTools || []
|
|
1726
|
+
};
|
|
1727
|
+
} catch (error) {
|
|
1728
|
+
if (error.code === "ENOENT") {
|
|
1729
|
+
globalConfig = { ...DEFAULT_CONFIG };
|
|
1730
|
+
} else if (error instanceof z.ZodError) {
|
|
1731
|
+
console.error("Global config validation error:", error.errors);
|
|
1732
|
+
console.error("Using default configuration");
|
|
1733
|
+
globalConfig = { ...DEFAULT_CONFIG };
|
|
1292
1734
|
} else {
|
|
1293
|
-
|
|
1735
|
+
throw error;
|
|
1294
1736
|
}
|
|
1295
1737
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
...DEFAULT_CONFIG.tools,
|
|
1308
|
-
...validated.tools
|
|
1309
|
-
},
|
|
1310
|
-
toolApiKeys: {
|
|
1311
|
-
...DEFAULT_CONFIG.toolApiKeys,
|
|
1312
|
-
...validated.toolApiKeys || {}
|
|
1313
|
-
},
|
|
1314
|
-
trustedTools: validated.trustedTools || []
|
|
1315
|
-
};
|
|
1738
|
+
this.projectConfigDir = findProjectConfigDir();
|
|
1739
|
+
let projectConfig = null;
|
|
1740
|
+
let projectLocalConfig = null;
|
|
1741
|
+
if (this.projectConfigDir) {
|
|
1742
|
+
projectConfig = await loadProjectConfig(this.projectConfigDir);
|
|
1743
|
+
projectLocalConfig = await loadProjectLocalConfig(this.projectConfigDir);
|
|
1744
|
+
if (projectConfig) {
|
|
1745
|
+
console.log(`\u{1F4C1} Project config loaded from: ${this.projectConfigDir}/.bike4mind/`);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
this.config = mergeConfigs(globalConfig, projectConfig, projectLocalConfig);
|
|
1316
1749
|
return this.config;
|
|
1317
1750
|
} catch (error) {
|
|
1318
1751
|
if (error.code === "ENOENT") {
|
|
@@ -1501,6 +1934,92 @@ var ConfigStore = class {
|
|
|
1501
1934
|
}
|
|
1502
1935
|
await this.save(config);
|
|
1503
1936
|
}
|
|
1937
|
+
/**
|
|
1938
|
+
* Get project config directory (if any)
|
|
1939
|
+
*/
|
|
1940
|
+
getProjectConfigDir() {
|
|
1941
|
+
return this.projectConfigDir;
|
|
1942
|
+
}
|
|
1943
|
+
/**
|
|
1944
|
+
* Initialize project config directory
|
|
1945
|
+
* Creates .bike4mind/ directory and ensures local.json is gitignored
|
|
1946
|
+
* Does NOT auto-create config.json (user creates that manually)
|
|
1947
|
+
*/
|
|
1948
|
+
async initProjectConfig() {
|
|
1949
|
+
const projectDir = this.projectConfigDir || findProjectConfigDir();
|
|
1950
|
+
if (!projectDir) {
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
const configDir = path4.join(projectDir, ".bike4mind");
|
|
1954
|
+
await fs4.mkdir(configDir, { recursive: true });
|
|
1955
|
+
await this.ensureGitignore(projectDir);
|
|
1956
|
+
}
|
|
1957
|
+
/**
|
|
1958
|
+
* Ensure .gitignore includes .bike4mind/local.json
|
|
1959
|
+
*/
|
|
1960
|
+
async ensureGitignore(projectDir) {
|
|
1961
|
+
const gitignorePath = path4.join(projectDir, ".gitignore");
|
|
1962
|
+
const entryToAdd = ".bike4mind/local.json";
|
|
1963
|
+
try {
|
|
1964
|
+
let gitignoreContent = "";
|
|
1965
|
+
try {
|
|
1966
|
+
gitignoreContent = await fs4.readFile(gitignorePath, "utf-8");
|
|
1967
|
+
} catch {
|
|
1968
|
+
}
|
|
1969
|
+
if (gitignoreContent.includes(entryToAdd)) {
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
const newContent = gitignoreContent.trim() + (gitignoreContent ? "\n" : "") + `
|
|
1973
|
+
# Bike4Mind local config (developer-specific)
|
|
1974
|
+
${entryToAdd}
|
|
1975
|
+
`;
|
|
1976
|
+
await fs4.writeFile(gitignorePath, newContent, "utf-8");
|
|
1977
|
+
console.log(`\u2705 Added ${entryToAdd} to .gitignore`);
|
|
1978
|
+
} catch (error) {
|
|
1979
|
+
console.warn(`\u26A0\uFE0F Failed to update .gitignore:`, error);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Save project config to .bike4mind/config.json
|
|
1984
|
+
*/
|
|
1985
|
+
async saveProjectConfig(config, projectDir) {
|
|
1986
|
+
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
1987
|
+
const configPath = path4.join(targetDir, ".bike4mind", "config.json");
|
|
1988
|
+
await fs4.mkdir(path4.dirname(configPath), { recursive: true });
|
|
1989
|
+
const validated = ProjectConfigSchema.parse(config);
|
|
1990
|
+
await fs4.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
1991
|
+
console.log(`\u2705 Saved project config to: ${configPath}`);
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Save project-local config to .bike4mind/local.json
|
|
1995
|
+
*/
|
|
1996
|
+
async saveProjectLocalConfig(config, projectDir) {
|
|
1997
|
+
const targetDir = projectDir || this.projectConfigDir || process.cwd();
|
|
1998
|
+
const configPath = path4.join(targetDir, ".bike4mind", "local.json");
|
|
1999
|
+
await fs4.mkdir(path4.dirname(configPath), { recursive: true });
|
|
2000
|
+
const validated = ProjectLocalConfigSchema.parse(config);
|
|
2001
|
+
await fs4.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
2002
|
+
await fs4.chmod(configPath, 384);
|
|
2003
|
+
console.log(`\u2705 Saved project-local config to: ${configPath}`);
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Load raw project config (without merging)
|
|
2007
|
+
*/
|
|
2008
|
+
async loadRawProjectConfig() {
|
|
2009
|
+
if (!this.projectConfigDir) {
|
|
2010
|
+
return null;
|
|
2011
|
+
}
|
|
2012
|
+
return loadProjectConfig(this.projectConfigDir);
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Load raw project-local config (without merging)
|
|
2016
|
+
*/
|
|
2017
|
+
async loadRawProjectLocalConfig() {
|
|
2018
|
+
if (!this.projectConfigDir) {
|
|
2019
|
+
return null;
|
|
2020
|
+
}
|
|
2021
|
+
return loadProjectLocalConfig(this.projectConfigDir);
|
|
2022
|
+
}
|
|
1504
2023
|
};
|
|
1505
2024
|
|
|
1506
2025
|
// src/storage/CommandHistoryStore.ts
|
|
@@ -7086,7 +7605,7 @@ var planetVisibilityTool = {
|
|
|
7086
7605
|
|
|
7087
7606
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
|
|
7088
7607
|
import { promises as fs7 } from "fs";
|
|
7089
|
-
import { existsSync as
|
|
7608
|
+
import { existsSync as existsSync3, statSync as statSync3 } from "fs";
|
|
7090
7609
|
import path6 from "path";
|
|
7091
7610
|
var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
|
|
7092
7611
|
async function readFileContent(params) {
|
|
@@ -7097,7 +7616,7 @@ async function readFileContent(params) {
|
|
|
7097
7616
|
if (!resolvedPath.startsWith(cwd)) {
|
|
7098
7617
|
throw new Error(`Access denied: Cannot read files outside of current working directory`);
|
|
7099
7618
|
}
|
|
7100
|
-
if (!
|
|
7619
|
+
if (!existsSync3(resolvedPath)) {
|
|
7101
7620
|
throw new Error(`File not found: ${filePath}`);
|
|
7102
7621
|
}
|
|
7103
7622
|
const stats = statSync3(resolvedPath);
|
|
@@ -7183,7 +7702,7 @@ var fileReadTool = {
|
|
|
7183
7702
|
|
|
7184
7703
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
|
|
7185
7704
|
import { promises as fs8 } from "fs";
|
|
7186
|
-
import { existsSync as
|
|
7705
|
+
import { existsSync as existsSync4 } from "fs";
|
|
7187
7706
|
import path7 from "path";
|
|
7188
7707
|
async function createFile(params) {
|
|
7189
7708
|
const { path: filePath, content, createDirectories = true } = params;
|
|
@@ -7193,7 +7712,7 @@ async function createFile(params) {
|
|
|
7193
7712
|
if (!resolvedPath.startsWith(cwd)) {
|
|
7194
7713
|
throw new Error(`Access denied: Cannot create files outside of current working directory`);
|
|
7195
7714
|
}
|
|
7196
|
-
const fileExists =
|
|
7715
|
+
const fileExists = existsSync4(resolvedPath);
|
|
7197
7716
|
const action = fileExists ? "overwritten" : "created";
|
|
7198
7717
|
if (createDirectories) {
|
|
7199
7718
|
const dir = path7.dirname(resolvedPath);
|
|
@@ -7211,7 +7730,7 @@ var createFileTool = {
|
|
|
7211
7730
|
implementation: (context) => ({
|
|
7212
7731
|
toolFn: async (value) => {
|
|
7213
7732
|
const params = value;
|
|
7214
|
-
const fileExists =
|
|
7733
|
+
const fileExists = existsSync4(path7.resolve(process.cwd(), path7.normalize(params.path)));
|
|
7215
7734
|
context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
|
|
7216
7735
|
path: params.path,
|
|
7217
7736
|
size: params.content.length
|
|
@@ -7591,7 +8110,7 @@ var grepSearchTool = {
|
|
|
7591
8110
|
|
|
7592
8111
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
7593
8112
|
import { promises as fs9 } from "fs";
|
|
7594
|
-
import { existsSync as
|
|
8113
|
+
import { existsSync as existsSync5, statSync as statSync6 } from "fs";
|
|
7595
8114
|
import path10 from "path";
|
|
7596
8115
|
async function deleteFile(params) {
|
|
7597
8116
|
const { path: filePath, recursive = false } = params;
|
|
@@ -7601,7 +8120,7 @@ async function deleteFile(params) {
|
|
|
7601
8120
|
if (!resolvedPath.startsWith(cwd)) {
|
|
7602
8121
|
throw new Error(`Access denied: Cannot delete files outside of current working directory`);
|
|
7603
8122
|
}
|
|
7604
|
-
if (!
|
|
8123
|
+
if (!existsSync5(resolvedPath)) {
|
|
7605
8124
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
7606
8125
|
}
|
|
7607
8126
|
const stats = statSync6(resolvedPath);
|
|
@@ -7625,7 +8144,7 @@ var deleteFileTool = {
|
|
|
7625
8144
|
toolFn: async (value) => {
|
|
7626
8145
|
const params = value;
|
|
7627
8146
|
const resolvedPath = path10.resolve(process.cwd(), path10.normalize(params.path));
|
|
7628
|
-
const isDirectory =
|
|
8147
|
+
const isDirectory = existsSync5(resolvedPath) && statSync6(resolvedPath).isDirectory();
|
|
7629
8148
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
7630
8149
|
path: params.path,
|
|
7631
8150
|
recursive: params.recursive
|
|
@@ -7915,10 +8434,10 @@ import jwt2 from "jsonwebtoken";
|
|
|
7915
8434
|
// src/utils/diffPreview.ts
|
|
7916
8435
|
import * as Diff from "diff";
|
|
7917
8436
|
import { readFile } from "fs/promises";
|
|
7918
|
-
import { existsSync as
|
|
8437
|
+
import { existsSync as existsSync6 } from "fs";
|
|
7919
8438
|
async function generateFileDiffPreview(args) {
|
|
7920
8439
|
try {
|
|
7921
|
-
if (!
|
|
8440
|
+
if (!existsSync6(args.path)) {
|
|
7922
8441
|
const lines2 = args.content.split("\n");
|
|
7923
8442
|
const preview = lines2.slice(0, 20).join("\n");
|
|
7924
8443
|
const hasMore = lines2.length > 20;
|
|
@@ -7948,7 +8467,7 @@ ${preview}${hasMore ? `
|
|
|
7948
8467
|
}
|
|
7949
8468
|
async function generateFileDeletePreview(args) {
|
|
7950
8469
|
try {
|
|
7951
|
-
if (!
|
|
8470
|
+
if (!existsSync6(args.path)) {
|
|
7952
8471
|
return `[File does not exist: ${args.path}]`;
|
|
7953
8472
|
}
|
|
7954
8473
|
const stats = await import("fs/promises").then((fs11) => fs11.stat(args.path));
|
|
@@ -8001,7 +8520,7 @@ var CliLogger = class extends Logger {
|
|
|
8001
8520
|
log(...args) {
|
|
8002
8521
|
}
|
|
8003
8522
|
};
|
|
8004
|
-
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext) {
|
|
8523
|
+
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore) {
|
|
8005
8524
|
const originalFn = tool.toolFn;
|
|
8006
8525
|
const toolName = tool.toolSchema.name;
|
|
8007
8526
|
return {
|
|
@@ -8045,7 +8564,25 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
8045
8564
|
throw new PermissionDeniedError(toolName, args);
|
|
8046
8565
|
}
|
|
8047
8566
|
if (response.action === "allow-always") {
|
|
8048
|
-
permissionManager.trustTool(toolName);
|
|
8567
|
+
const canTrust = permissionManager.trustTool(toolName);
|
|
8568
|
+
if (canTrust) {
|
|
8569
|
+
const projectDir = configStore.getProjectConfigDir();
|
|
8570
|
+
if (projectDir) {
|
|
8571
|
+
try {
|
|
8572
|
+
await configStore.initProjectConfig();
|
|
8573
|
+
const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
|
|
8574
|
+
const updatedLocal = {
|
|
8575
|
+
...existingLocal,
|
|
8576
|
+
trustedTools: [...existingLocal.trustedTools || [], toolName]
|
|
8577
|
+
};
|
|
8578
|
+
await configStore.saveProjectLocalConfig(updatedLocal);
|
|
8579
|
+
} catch (error) {
|
|
8580
|
+
await configStore.trustTool(toolName);
|
|
8581
|
+
}
|
|
8582
|
+
} else {
|
|
8583
|
+
await configStore.trustTool(toolName);
|
|
8584
|
+
}
|
|
8585
|
+
}
|
|
8049
8586
|
}
|
|
8050
8587
|
const result = await originalFn(args);
|
|
8051
8588
|
agentContext.observationQueue.push({ toolName, result });
|
|
@@ -8053,7 +8590,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
8053
8590
|
}
|
|
8054
8591
|
};
|
|
8055
8592
|
}
|
|
8056
|
-
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, toolApiKeys) {
|
|
8593
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, toolApiKeys) {
|
|
8057
8594
|
const logger2 = new CliLogger();
|
|
8058
8595
|
const storage = new NoOpStorage();
|
|
8059
8596
|
const user = {
|
|
@@ -8137,7 +8674,9 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
8137
8674
|
toolConfig,
|
|
8138
8675
|
model
|
|
8139
8676
|
);
|
|
8140
|
-
const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
|
|
8677
|
+
const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
|
|
8678
|
+
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
|
|
8679
|
+
);
|
|
8141
8680
|
return { tools: tools2, agentContext };
|
|
8142
8681
|
}
|
|
8143
8682
|
|
|
@@ -8202,23 +8741,30 @@ function canTrustTool(toolName, customCategories) {
|
|
|
8202
8741
|
|
|
8203
8742
|
// src/utils/PermissionManager.ts
|
|
8204
8743
|
var PermissionManager = class {
|
|
8205
|
-
constructor(trustedTools = [], customCategories) {
|
|
8744
|
+
constructor(trustedTools = [], customCategories, deniedTools) {
|
|
8206
8745
|
this.trustedTools = /* @__PURE__ */ new Set();
|
|
8746
|
+
this.deniedTools = /* @__PURE__ */ new Set();
|
|
8207
8747
|
this.trustedTools = new Set(trustedTools);
|
|
8208
8748
|
this.customCategories = new Map(Object.entries(customCategories || {}));
|
|
8749
|
+
this.deniedTools = new Set(deniedTools || []);
|
|
8209
8750
|
}
|
|
8210
8751
|
/**
|
|
8211
8752
|
* Check if a tool needs permission before execution
|
|
8212
8753
|
*
|
|
8213
8754
|
* A tool needs permission if:
|
|
8214
|
-
* 1. It's
|
|
8215
|
-
* 2. It's not in the
|
|
8755
|
+
* 1. It's in the denied tools list (from project config), OR
|
|
8756
|
+
* 2. It's not in the auto_approve category, AND
|
|
8757
|
+
* 3. It's not in the trusted tools set
|
|
8216
8758
|
*
|
|
8217
8759
|
* Note: prompt_always tools CANNOT be trusted, so they always need permission
|
|
8760
|
+
* Note: denied tools from project config ALWAYS need permission (cannot be overridden locally)
|
|
8218
8761
|
*/
|
|
8219
8762
|
needsPermission(toolName) {
|
|
8220
8763
|
const categoryMap = Object.fromEntries(this.customCategories);
|
|
8221
8764
|
const category = getToolCategory(toolName, categoryMap);
|
|
8765
|
+
if (this.deniedTools.has(toolName)) {
|
|
8766
|
+
return true;
|
|
8767
|
+
}
|
|
8222
8768
|
if (category === "auto_approve") {
|
|
8223
8769
|
return false;
|
|
8224
8770
|
}
|
|
@@ -8229,10 +8775,13 @@ var PermissionManager = class {
|
|
|
8229
8775
|
}
|
|
8230
8776
|
/**
|
|
8231
8777
|
* Add a tool to the trusted list
|
|
8232
|
-
* Returns false if the tool cannot be trusted (prompt_always category)
|
|
8778
|
+
* Returns false if the tool cannot be trusted (prompt_always category or denied by project)
|
|
8233
8779
|
*/
|
|
8234
8780
|
trustTool(toolName) {
|
|
8235
8781
|
const categoryMap = Object.fromEntries(this.customCategories);
|
|
8782
|
+
if (this.deniedTools.has(toolName)) {
|
|
8783
|
+
return false;
|
|
8784
|
+
}
|
|
8236
8785
|
if (!canTrustTool(toolName, categoryMap)) {
|
|
8237
8786
|
return false;
|
|
8238
8787
|
}
|
|
@@ -8265,12 +8814,27 @@ var PermissionManager = class {
|
|
|
8265
8814
|
return getToolCategory(toolName, categoryMap);
|
|
8266
8815
|
}
|
|
8267
8816
|
/**
|
|
8268
|
-
* Check if a tool can be trusted (not in prompt_always category)
|
|
8817
|
+
* Check if a tool can be trusted (not in prompt_always category or denied by project)
|
|
8269
8818
|
*/
|
|
8270
8819
|
canBeTrusted(toolName) {
|
|
8271
8820
|
const categoryMap = Object.fromEntries(this.customCategories);
|
|
8821
|
+
if (this.deniedTools.has(toolName)) {
|
|
8822
|
+
return false;
|
|
8823
|
+
}
|
|
8272
8824
|
return canTrustTool(toolName, categoryMap);
|
|
8273
8825
|
}
|
|
8826
|
+
/**
|
|
8827
|
+
* Check if a tool is denied by project config
|
|
8828
|
+
*/
|
|
8829
|
+
isDenied(toolName) {
|
|
8830
|
+
return this.deniedTools.has(toolName);
|
|
8831
|
+
}
|
|
8832
|
+
/**
|
|
8833
|
+
* Get list of denied tools
|
|
8834
|
+
*/
|
|
8835
|
+
getDeniedTools() {
|
|
8836
|
+
return Array.from(this.deniedTools).sort();
|
|
8837
|
+
}
|
|
8274
8838
|
/**
|
|
8275
8839
|
* Clear all trusted tools
|
|
8276
8840
|
*/
|
|
@@ -8565,7 +9129,7 @@ var logger = Logger2.getInstance();
|
|
|
8565
9129
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
8566
9130
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
8567
9131
|
import path12 from "path";
|
|
8568
|
-
import { existsSync as
|
|
9132
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3 } from "fs";
|
|
8569
9133
|
var MCPClient = class {
|
|
8570
9134
|
// Note: This class handles MCP server communication with repository filtering
|
|
8571
9135
|
mcp;
|
|
@@ -8601,7 +9165,7 @@ var MCPClient = class {
|
|
|
8601
9165
|
path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
8602
9166
|
path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
8603
9167
|
];
|
|
8604
|
-
const serverScriptPath = candidatePaths.find((p) =>
|
|
9168
|
+
const serverScriptPath = candidatePaths.find((p) => existsSync7(p));
|
|
8605
9169
|
if (!serverScriptPath) {
|
|
8606
9170
|
const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
8607
9171
|
console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
|
|
@@ -9183,7 +9747,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
9183
9747
|
// package.json
|
|
9184
9748
|
var package_default = {
|
|
9185
9749
|
name: "@bike4mind/cli",
|
|
9186
|
-
version: "0.2.3-feat-cli-
|
|
9750
|
+
version: "0.2.3-feat-cli-project-scoped-config.17063+4e29d03e0",
|
|
9187
9751
|
type: "module",
|
|
9188
9752
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
9189
9753
|
license: "UNLICENSED",
|
|
@@ -9285,10 +9849,10 @@ var package_default = {
|
|
|
9285
9849
|
},
|
|
9286
9850
|
devDependencies: {
|
|
9287
9851
|
"@bike4mind/agents": "0.1.0",
|
|
9288
|
-
"@bike4mind/common": "2.37.1-feat-cli-
|
|
9289
|
-
"@bike4mind/mcp": "1.19.5-feat-cli-
|
|
9290
|
-
"@bike4mind/services": "2.31.1-feat-cli-
|
|
9291
|
-
"@bike4mind/utils": "2.0.4-feat-cli-
|
|
9852
|
+
"@bike4mind/common": "2.37.1-feat-cli-project-scoped-config.17063+4e29d03e0",
|
|
9853
|
+
"@bike4mind/mcp": "1.19.5-feat-cli-project-scoped-config.17063+4e29d03e0",
|
|
9854
|
+
"@bike4mind/services": "2.31.1-feat-cli-project-scoped-config.17063+4e29d03e0",
|
|
9855
|
+
"@bike4mind/utils": "2.0.4-feat-cli-project-scoped-config.17063+4e29d03e0",
|
|
9292
9856
|
"@types/diff": "^5.0.9",
|
|
9293
9857
|
"@types/jsonwebtoken": "^9.0.4",
|
|
9294
9858
|
"@types/node": "^22.9.0",
|
|
@@ -9300,7 +9864,7 @@ var package_default = {
|
|
|
9300
9864
|
typescript: "^5.9.3",
|
|
9301
9865
|
vitest: "^3.2.4"
|
|
9302
9866
|
},
|
|
9303
|
-
gitHead: "
|
|
9867
|
+
gitHead: "4e29d03e04acb4714606940d6557b9601325ed73"
|
|
9304
9868
|
};
|
|
9305
9869
|
|
|
9306
9870
|
// src/config/constants.ts
|
|
@@ -9321,7 +9885,7 @@ var EXIT_TIMEOUT_MS = 2e3;
|
|
|
9321
9885
|
var usageCache = null;
|
|
9322
9886
|
function CliApp() {
|
|
9323
9887
|
const { exit } = useApp();
|
|
9324
|
-
const [state, setState] =
|
|
9888
|
+
const [state, setState] = useState4({
|
|
9325
9889
|
session: null,
|
|
9326
9890
|
sessionStore: new SessionStore(),
|
|
9327
9891
|
configStore: new ConfigStore(),
|
|
@@ -9329,13 +9893,15 @@ function CliApp() {
|
|
|
9329
9893
|
agent: null,
|
|
9330
9894
|
mcpManager: null,
|
|
9331
9895
|
permissionManager: null,
|
|
9332
|
-
permissionPrompt: null
|
|
9896
|
+
permissionPrompt: null,
|
|
9897
|
+
trustLocationSelector: null
|
|
9333
9898
|
});
|
|
9334
|
-
const [isInitialized, setIsInitialized] =
|
|
9335
|
-
const [initError, setInitError] =
|
|
9336
|
-
const [commandHistory, setCommandHistory] =
|
|
9899
|
+
const [isInitialized, setIsInitialized] = useState4(false);
|
|
9900
|
+
const [initError, setInitError] = useState4(null);
|
|
9901
|
+
const [commandHistory, setCommandHistory] = useState4([]);
|
|
9337
9902
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
9338
9903
|
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
9904
|
+
const setShowConfigEditor = useCliStore((state2) => state2.setShowConfigEditor);
|
|
9339
9905
|
const setExitRequested = useCliStore((state2) => state2.setExitRequested);
|
|
9340
9906
|
const performCleanup = useCallback(async () => {
|
|
9341
9907
|
const cleanupTasks = [];
|
|
@@ -9365,7 +9931,7 @@ function CliApp() {
|
|
|
9365
9931
|
process.exit(0);
|
|
9366
9932
|
}, 100);
|
|
9367
9933
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent]);
|
|
9368
|
-
|
|
9934
|
+
useInput3((input, key) => {
|
|
9369
9935
|
if (key.ctrl && input === "c") {
|
|
9370
9936
|
const now = Date.now();
|
|
9371
9937
|
if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
|
|
@@ -9490,7 +10056,13 @@ function CliApp() {
|
|
|
9490
10056
|
debug: () => {
|
|
9491
10057
|
}
|
|
9492
10058
|
};
|
|
9493
|
-
const permissionManager = new PermissionManager(
|
|
10059
|
+
const permissionManager = new PermissionManager(
|
|
10060
|
+
config.trustedTools || [],
|
|
10061
|
+
void 0,
|
|
10062
|
+
// customCategories (not used yet)
|
|
10063
|
+
config.tools.disabled || []
|
|
10064
|
+
// denied tools from project config
|
|
10065
|
+
);
|
|
9494
10066
|
const promptFn = (toolName, args, preview) => {
|
|
9495
10067
|
return new Promise((resolve3) => {
|
|
9496
10068
|
const canBeTrusted = permissionManager.canBeTrusted(toolName);
|
|
@@ -9518,6 +10090,7 @@ function CliApp() {
|
|
|
9518
10090
|
permissionManager,
|
|
9519
10091
|
promptFn,
|
|
9520
10092
|
agentContext,
|
|
10093
|
+
state.configStore,
|
|
9521
10094
|
config.toolApiKeys
|
|
9522
10095
|
);
|
|
9523
10096
|
console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
|
|
@@ -9562,13 +10135,14 @@ EXAMPLES:
|
|
|
9562
10135
|
- "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
|
|
9563
10136
|
|
|
9564
10137
|
Remember: Use context from previous messages to understand follow-up questions.`;
|
|
10138
|
+
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
9565
10139
|
const agent = new ReActAgent({
|
|
9566
10140
|
userId: config.userId,
|
|
9567
10141
|
logger: silentLogger,
|
|
9568
10142
|
llm,
|
|
9569
10143
|
model: modelInfo.id,
|
|
9570
10144
|
tools: allTools,
|
|
9571
|
-
maxIterations
|
|
10145
|
+
maxIterations,
|
|
9572
10146
|
maxTokens: config.preferences.maxTokens,
|
|
9573
10147
|
temperature: config.preferences.temperature,
|
|
9574
10148
|
systemPrompt: cliSystemPrompt
|
|
@@ -9680,7 +10254,6 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
9680
10254
|
Available commands:
|
|
9681
10255
|
/help - Show this help message
|
|
9682
10256
|
/exit - Exit the CLI
|
|
9683
|
-
/clear - Start a new session
|
|
9684
10257
|
/login - Authenticate with your B4M account
|
|
9685
10258
|
/logout - Clear authentication and sign out
|
|
9686
10259
|
/whoami - Show current authenticated user
|
|
@@ -9698,6 +10271,9 @@ Tool Permissions:
|
|
|
9698
10271
|
/trust <tool-name> - Trust a tool (won't ask permission again)
|
|
9699
10272
|
/untrust <tool-name> - Remove tool from trusted list
|
|
9700
10273
|
/trusted - List all trusted tools
|
|
10274
|
+
|
|
10275
|
+
Project Configuration:
|
|
10276
|
+
/project-config - Show merged project configuration
|
|
9701
10277
|
`);
|
|
9702
10278
|
break;
|
|
9703
10279
|
case "exit":
|
|
@@ -9726,20 +10302,7 @@ Tool Permissions:
|
|
|
9726
10302
|
console.log("");
|
|
9727
10303
|
break;
|
|
9728
10304
|
case "config": {
|
|
9729
|
-
|
|
9730
|
-
console.log("\n\u2699\uFE0F Configuration:\n");
|
|
9731
|
-
console.log(`Default Model: ${config.defaultModel}`);
|
|
9732
|
-
console.log(`Max Tokens: ${config.preferences.maxTokens}`);
|
|
9733
|
-
console.log(`Temperature: ${config.preferences.temperature}`);
|
|
9734
|
-
const apiUrl = getApiUrl(config.apiConfig);
|
|
9735
|
-
const apiType = getEnvironmentName(config.apiConfig);
|
|
9736
|
-
console.log(`API: ${apiType} (${apiUrl})`);
|
|
9737
|
-
console.log("");
|
|
9738
|
-
console.log("Related commands:");
|
|
9739
|
-
console.log(" /api-info - Show detailed API configuration");
|
|
9740
|
-
console.log(" /set-api - Connect to self-hosted instance");
|
|
9741
|
-
console.log(" /reset-api - Reset to Bike4Mind main service");
|
|
9742
|
-
console.log("");
|
|
10305
|
+
setShowConfigEditor(true);
|
|
9743
10306
|
break;
|
|
9744
10307
|
}
|
|
9745
10308
|
case "set-api": {
|
|
@@ -9789,7 +10352,7 @@ Tool Permissions:
|
|
|
9789
10352
|
console.log("");
|
|
9790
10353
|
break;
|
|
9791
10354
|
}
|
|
9792
|
-
case "trust":
|
|
10355
|
+
case "trust": {
|
|
9793
10356
|
if (!state.permissionManager) {
|
|
9794
10357
|
console.log("Permission manager not initialized");
|
|
9795
10358
|
return;
|
|
@@ -9797,16 +10360,85 @@ Tool Permissions:
|
|
|
9797
10360
|
const toolToTrust = args[0];
|
|
9798
10361
|
if (!toolToTrust) {
|
|
9799
10362
|
console.log("Usage: /trust <tool-name>");
|
|
10363
|
+
console.log("");
|
|
10364
|
+
console.log("Example:");
|
|
10365
|
+
console.log(" /trust file_read - Opens interactive location selector");
|
|
10366
|
+
console.log("");
|
|
9800
10367
|
return;
|
|
9801
10368
|
}
|
|
9802
|
-
const
|
|
9803
|
-
if (
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
10369
|
+
const canTrust = state.permissionManager.trustTool(toolToTrust);
|
|
10370
|
+
if (!canTrust) {
|
|
10371
|
+
if (state.permissionManager.isDenied(toolToTrust)) {
|
|
10372
|
+
console.log(`\u274C Tool '${toolToTrust}' is denied by project configuration and cannot be trusted`);
|
|
10373
|
+
} else {
|
|
10374
|
+
console.log(`\u274C Tool '${toolToTrust}' cannot be trusted (it's in the 'prompt_always' category)`);
|
|
10375
|
+
}
|
|
10376
|
+
return;
|
|
9808
10377
|
}
|
|
10378
|
+
state.permissionManager.untrustTool(toolToTrust);
|
|
10379
|
+
const handleLocationSelect = async (location) => {
|
|
10380
|
+
setState((prev) => ({ ...prev, trustLocationSelector: null }));
|
|
10381
|
+
if (!location) {
|
|
10382
|
+
console.log("\u274C Trust operation cancelled");
|
|
10383
|
+
return;
|
|
10384
|
+
}
|
|
10385
|
+
const projectDir = state.configStore.getProjectConfigDir();
|
|
10386
|
+
switch (location) {
|
|
10387
|
+
case "local": {
|
|
10388
|
+
if (!projectDir) {
|
|
10389
|
+
console.log('\u274C No project found. Use "global" to save to ~/.bike4mind/config.json');
|
|
10390
|
+
return;
|
|
10391
|
+
}
|
|
10392
|
+
await state.configStore.initProjectConfig();
|
|
10393
|
+
const existingLocal = await state.configStore.loadRawProjectLocalConfig() || {};
|
|
10394
|
+
const updatedLocal = {
|
|
10395
|
+
...existingLocal,
|
|
10396
|
+
trustedTools: [...existingLocal.trustedTools || [], toolToTrust]
|
|
10397
|
+
};
|
|
10398
|
+
await state.configStore.saveProjectLocalConfig(updatedLocal);
|
|
10399
|
+
state.permissionManager?.trustTool(toolToTrust);
|
|
10400
|
+
console.log(`\u2705 Tool '${toolToTrust}' trusted for this project only`);
|
|
10401
|
+
console.log(` Saved in: ${projectDir}/.bike4mind/local.json`);
|
|
10402
|
+
break;
|
|
10403
|
+
}
|
|
10404
|
+
case "project": {
|
|
10405
|
+
if (!projectDir) {
|
|
10406
|
+
console.log('\u274C No project found. Use "global" to save to ~/.bike4mind/config.json');
|
|
10407
|
+
return;
|
|
10408
|
+
}
|
|
10409
|
+
const existingProject = await state.configStore.loadRawProjectConfig() || {};
|
|
10410
|
+
const updatedProject = {
|
|
10411
|
+
...existingProject,
|
|
10412
|
+
tools: {
|
|
10413
|
+
...existingProject.tools,
|
|
10414
|
+
enabled: [...existingProject.tools?.enabled || [], toolToTrust]
|
|
10415
|
+
}
|
|
10416
|
+
};
|
|
10417
|
+
await state.configStore.saveProjectConfig(updatedProject);
|
|
10418
|
+
state.permissionManager?.trustTool(toolToTrust);
|
|
10419
|
+
console.log(`\u2705 Tool '${toolToTrust}' trusted for entire team`);
|
|
10420
|
+
console.log(` Saved in: ${projectDir}/.bike4mind/config.json`);
|
|
10421
|
+
console.log(" \u26A0\uFE0F Remember to commit this file to share with your team!");
|
|
10422
|
+
break;
|
|
10423
|
+
}
|
|
10424
|
+
case "global": {
|
|
10425
|
+
await state.configStore.trustTool(toolToTrust);
|
|
10426
|
+
state.permissionManager?.trustTool(toolToTrust);
|
|
10427
|
+
console.log(`\u2705 Tool '${toolToTrust}' trusted globally (all projects)`);
|
|
10428
|
+
console.log(" Saved in: ~/.bike4mind/config.json");
|
|
10429
|
+
break;
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
};
|
|
10433
|
+
setState((prev) => ({
|
|
10434
|
+
...prev,
|
|
10435
|
+
trustLocationSelector: {
|
|
10436
|
+
toolName: toolToTrust,
|
|
10437
|
+
resolve: handleLocationSelect
|
|
10438
|
+
}
|
|
10439
|
+
}));
|
|
9809
10440
|
break;
|
|
10441
|
+
}
|
|
9810
10442
|
case "untrust":
|
|
9811
10443
|
if (!state.permissionManager) {
|
|
9812
10444
|
console.log("Permission manager not initialized");
|
|
@@ -9855,35 +10487,6 @@ Tool Permissions:
|
|
|
9855
10487
|
console.log(`Expires: ${new Date(authTokens.expiresAt).toLocaleString()}`);
|
|
9856
10488
|
console.log("");
|
|
9857
10489
|
break;
|
|
9858
|
-
case "clear":
|
|
9859
|
-
case "new": {
|
|
9860
|
-
console.clear();
|
|
9861
|
-
const model = state.session?.model || state.config?.defaultModel || "claude-sonnet";
|
|
9862
|
-
const newSession = {
|
|
9863
|
-
id: uuidv49(),
|
|
9864
|
-
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
9865
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9866
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9867
|
-
model,
|
|
9868
|
-
messages: [],
|
|
9869
|
-
metadata: {
|
|
9870
|
-
totalTokens: 0,
|
|
9871
|
-
totalCost: 0,
|
|
9872
|
-
toolCallCount: 0
|
|
9873
|
-
}
|
|
9874
|
-
};
|
|
9875
|
-
await logger.initialize(newSession.id);
|
|
9876
|
-
logger.debug("=== New Session Started via /clear ===");
|
|
9877
|
-
setState((prev) => ({ ...prev, session: newSession }));
|
|
9878
|
-
setStoreSession(newSession);
|
|
9879
|
-
useCliStore.getState().clearAgentSteps();
|
|
9880
|
-
usageCache = null;
|
|
9881
|
-
console.log("New session started.");
|
|
9882
|
-
console.log(`
|
|
9883
|
-
\u{1F4DD} Session: ${newSession.name} | \u{1F916} Model: ${newSession.model} | \u{1F4CA} Tokens: 0
|
|
9884
|
-
`);
|
|
9885
|
-
break;
|
|
9886
|
-
}
|
|
9887
10490
|
case "usage": {
|
|
9888
10491
|
const usageAuthTokens = await state.configStore.getAuthTokens();
|
|
9889
10492
|
if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
@@ -9978,6 +10581,49 @@ Usage by Model (Last ${USAGE_DAYS} days):`);
|
|
|
9978
10581
|
console.log(`
|
|
9979
10582
|
No usage data available for the last ${USAGE_DAYS} days.`);
|
|
9980
10583
|
}
|
|
10584
|
+
break;
|
|
10585
|
+
}
|
|
10586
|
+
case "project-config": {
|
|
10587
|
+
const projectDir = state.configStore.getProjectConfigDir();
|
|
10588
|
+
const config = await state.configStore.get();
|
|
10589
|
+
console.log("\n\u{1F4C1} Project Configuration:\n");
|
|
10590
|
+
if (projectDir) {
|
|
10591
|
+
console.log(`Project Directory: ${projectDir}/.bike4mind/`);
|
|
10592
|
+
console.log("");
|
|
10593
|
+
const projectConfig = await state.configStore.loadRawProjectConfig();
|
|
10594
|
+
const localConfig = await state.configStore.loadRawProjectLocalConfig();
|
|
10595
|
+
if (projectConfig) {
|
|
10596
|
+
console.log("Team Config (.bike4mind/config.json):");
|
|
10597
|
+
if (projectConfig.defaultModel) {
|
|
10598
|
+
console.log(` Default Model: ${projectConfig.defaultModel}`);
|
|
10599
|
+
}
|
|
10600
|
+
if (projectConfig.tools?.denied && projectConfig.tools.denied.length > 0) {
|
|
10601
|
+
console.log(` Denied Tools: ${projectConfig.tools.denied.join(", ")}`);
|
|
10602
|
+
}
|
|
10603
|
+
if (projectConfig.mcpServers && projectConfig.mcpServers.length > 0) {
|
|
10604
|
+
console.log(` MCP Servers: ${projectConfig.mcpServers.map((s) => s.name).join(", ")}`);
|
|
10605
|
+
}
|
|
10606
|
+
console.log("");
|
|
10607
|
+
}
|
|
10608
|
+
if (localConfig) {
|
|
10609
|
+
console.log("Local Config (.bike4mind/local.json):");
|
|
10610
|
+
if (localConfig.trustedTools && localConfig.trustedTools.length > 0) {
|
|
10611
|
+
console.log(` Trusted Tools: ${localConfig.trustedTools.join(", ")}`);
|
|
10612
|
+
}
|
|
10613
|
+
if (localConfig.mcpServers && localConfig.mcpServers.length > 0) {
|
|
10614
|
+
console.log(` MCP Servers: ${localConfig.mcpServers.map((s) => s.name).join(", ")}`);
|
|
10615
|
+
}
|
|
10616
|
+
console.log("");
|
|
10617
|
+
}
|
|
10618
|
+
console.log("Merged Configuration (what you're using):");
|
|
10619
|
+
console.log(` Default Model: ${config.defaultModel}`);
|
|
10620
|
+
console.log(` Trusted Tools: ${config.trustedTools?.length || 0} tools`);
|
|
10621
|
+
console.log(` Denied Tools: ${config.tools.disabled.length} tools`);
|
|
10622
|
+
console.log(` MCP Servers: ${config.mcpServers.length} servers`);
|
|
10623
|
+
} else {
|
|
10624
|
+
console.log("No project configuration found.");
|
|
10625
|
+
console.log("Tip: Create .bike4mind/config.json in your project root for team-wide settings.");
|
|
10626
|
+
}
|
|
9981
10627
|
console.log("");
|
|
9982
10628
|
break;
|
|
9983
10629
|
}
|
|
@@ -9986,12 +10632,36 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
9986
10632
|
console.log("Type /help for available commands");
|
|
9987
10633
|
}
|
|
9988
10634
|
};
|
|
10635
|
+
const handleSaveConfig = async (updatedConfig) => {
|
|
10636
|
+
await state.configStore.save(updatedConfig);
|
|
10637
|
+
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
10638
|
+
};
|
|
9989
10639
|
if (initError) {
|
|
9990
|
-
return /* @__PURE__ */
|
|
10640
|
+
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"));
|
|
10641
|
+
}
|
|
10642
|
+
if (state.trustLocationSelector) {
|
|
10643
|
+
const projectDir = state.configStore.getProjectConfigDir();
|
|
10644
|
+
const inProject = projectDir !== null;
|
|
10645
|
+
return /* @__PURE__ */ React13.createElement(
|
|
10646
|
+
TrustLocationSelector,
|
|
10647
|
+
{
|
|
10648
|
+
inProject,
|
|
10649
|
+
onSelect: (location) => {
|
|
10650
|
+
if (state.trustLocationSelector) {
|
|
10651
|
+
state.trustLocationSelector.resolve(location);
|
|
10652
|
+
}
|
|
10653
|
+
},
|
|
10654
|
+
onCancel: () => {
|
|
10655
|
+
if (state.trustLocationSelector) {
|
|
10656
|
+
state.trustLocationSelector.resolve(null);
|
|
10657
|
+
}
|
|
10658
|
+
}
|
|
10659
|
+
}
|
|
10660
|
+
);
|
|
9991
10661
|
}
|
|
9992
10662
|
if (state.showLoginFlow) {
|
|
9993
10663
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
9994
|
-
return /* @__PURE__ */
|
|
10664
|
+
return /* @__PURE__ */ React13.createElement(
|
|
9995
10665
|
LoginFlow,
|
|
9996
10666
|
{
|
|
9997
10667
|
apiUrl: loginApiUrl,
|
|
@@ -10014,14 +10684,16 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
10014
10684
|
);
|
|
10015
10685
|
}
|
|
10016
10686
|
if (!isInitialized) {
|
|
10017
|
-
return /* @__PURE__ */
|
|
10687
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "\u{1F680} Initializing..."));
|
|
10018
10688
|
}
|
|
10019
|
-
return /* @__PURE__ */
|
|
10689
|
+
return /* @__PURE__ */ React13.createElement(
|
|
10020
10690
|
App,
|
|
10021
10691
|
{
|
|
10022
10692
|
onMessage: handleMessage,
|
|
10023
10693
|
onCommand: handleCommand,
|
|
10024
10694
|
commandHistory,
|
|
10695
|
+
config: state.config,
|
|
10696
|
+
onSaveConfig: handleSaveConfig,
|
|
10025
10697
|
onPermissionResponse: (response) => {
|
|
10026
10698
|
if (state.permissionPrompt) {
|
|
10027
10699
|
state.permissionPrompt.resolve({ action: response });
|
|
@@ -10047,6 +10719,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
10047
10719
|
if (isDevMode) {
|
|
10048
10720
|
console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
10049
10721
|
}
|
|
10050
|
-
render(/* @__PURE__ */
|
|
10722
|
+
render(/* @__PURE__ */ React13.createElement(CliApp, null), {
|
|
10051
10723
|
exitOnCtrlC: false
|
|
10052
10724
|
});
|