@bike4mind/cli 0.2.5-fix-github-issue-type.17095 → 0.2.6-fix-cli-mcp-type-mismatch.17119

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-MPZQL3KY.js";
8
- import "./chunk-PKOGN2ZZ.js";
9
- import "./chunk-TPWKBMIZ.js";
7
+ } from "./chunk-3CCBDIFR.js";
8
+ import "./chunk-LIRY24M5.js";
9
+ import "./chunk-YXHQN7SK.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,7 +15,7 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-KH2C4VVX.js";
18
+ } from "./chunk-DNSBC3LO.js";
19
19
  import {
20
20
  Logger
21
21
  } from "./chunk-AMDXHL6S.js";
@@ -73,14 +73,14 @@ import {
73
73
  XAI_IMAGE_MODELS,
74
74
  b4mLLMTools,
75
75
  getMcpProviderMetadata
76
- } from "./chunk-7Y3E4OY6.js";
76
+ } from "./chunk-H6SHASMZ.js";
77
77
  import {
78
78
  __require
79
79
  } from "./chunk-PDX44BCA.js";
80
80
 
81
81
  // src/index.tsx
82
- import React12, { useState as useState4, useEffect as useEffect3, useCallback } from "react";
83
- import { render, Box as Box12, Text as Text11, useApp, useInput as useInput3 } from "ink";
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
@@ -685,103 +685,128 @@ var EXPORT_FORMAT_OPTIONS = [
685
685
  { label: "markdown", value: "markdown" },
686
686
  { label: "json", value: "json" }
687
687
  ];
688
- var CONFIG_ITEMS = [
689
- {
690
- key: "maxIterations",
691
- label: "Max Iterations",
692
- type: "select",
693
- options: MAX_ITERATIONS_OPTIONS,
694
- getValue: (config) => config.preferences.maxIterations,
695
- setValue: (config, value) => ({
696
- ...config,
697
- preferences: {
698
- ...config.preferences,
699
- maxIterations: value
700
- }
701
- })
702
- },
703
- {
704
- key: "maxTokens",
705
- label: "Max Tokens",
706
- type: "number",
707
- min: 256,
708
- max: 128e3,
709
- step: 256,
710
- getValue: (config) => config.preferences.maxTokens,
711
- setValue: (config, value) => ({
712
- ...config,
713
- preferences: {
714
- ...config.preferences,
715
- maxTokens: value
716
- }
717
- })
718
- },
719
- {
720
- key: "temperature",
721
- label: "Temperature",
722
- type: "number",
723
- min: 0,
724
- max: 2,
725
- step: 0.1,
726
- getValue: (config) => config.preferences.temperature,
727
- setValue: (config, value) => ({
728
- ...config,
729
- preferences: {
730
- ...config.preferences,
731
- temperature: value
732
- }
733
- })
734
- },
735
- {
736
- key: "autoSave",
737
- label: "Auto Save",
738
- type: "boolean",
739
- getValue: (config) => config.preferences.autoSave,
740
- setValue: (config, value) => ({
741
- ...config,
742
- preferences: {
743
- ...config.preferences,
744
- autoSave: value
745
- }
746
- })
747
- },
748
- {
749
- key: "theme",
750
- label: "Theme",
751
- type: "select",
752
- options: THEME_OPTIONS,
753
- getValue: (config) => config.preferences.theme,
754
- setValue: (config, value) => ({
755
- ...config,
756
- preferences: {
757
- ...config.preferences,
758
- theme: value
759
- }
760
- })
761
- },
762
- {
763
- key: "exportFormat",
764
- label: "Export Format",
765
- type: "select",
766
- options: EXPORT_FORMAT_OPTIONS,
767
- getValue: (config) => config.preferences.exportFormat,
768
- setValue: (config, value) => ({
769
- ...config,
770
- preferences: {
771
- ...config.preferences,
772
- exportFormat: value
773
- }
774
- })
688
+ function buildConfigItems(availableModels) {
689
+ const modelOptions = availableModels.map((model) => ({
690
+ label: model.name,
691
+ value: model.id
692
+ }));
693
+ const items = [];
694
+ if (modelOptions.length > 0) {
695
+ items.push({
696
+ key: "model",
697
+ label: "Model",
698
+ type: "select",
699
+ options: modelOptions,
700
+ getValue: (config) => {
701
+ const modelExists = modelOptions.some((opt) => opt.value === config.defaultModel);
702
+ return modelExists ? config.defaultModel : modelOptions[0]?.value ?? config.defaultModel;
703
+ },
704
+ setValue: (config, value) => ({
705
+ ...config,
706
+ defaultModel: value
707
+ })
708
+ });
775
709
  }
776
- ];
777
- function ConfigEditor({ config, onSave, onClose }) {
710
+ items.push(
711
+ {
712
+ key: "maxIterations",
713
+ label: "Max Iterations",
714
+ type: "select",
715
+ options: MAX_ITERATIONS_OPTIONS,
716
+ getValue: (config) => config.preferences.maxIterations,
717
+ setValue: (config, value) => ({
718
+ ...config,
719
+ preferences: {
720
+ ...config.preferences,
721
+ maxIterations: value
722
+ }
723
+ })
724
+ },
725
+ {
726
+ key: "maxTokens",
727
+ label: "Max Tokens",
728
+ type: "number",
729
+ min: 256,
730
+ max: 128e3,
731
+ step: 256,
732
+ getValue: (config) => config.preferences.maxTokens,
733
+ setValue: (config, value) => ({
734
+ ...config,
735
+ preferences: {
736
+ ...config.preferences,
737
+ maxTokens: value
738
+ }
739
+ })
740
+ },
741
+ {
742
+ key: "temperature",
743
+ label: "Temperature",
744
+ type: "number",
745
+ min: 0,
746
+ max: 2,
747
+ step: 0.1,
748
+ getValue: (config) => config.preferences.temperature,
749
+ setValue: (config, value) => ({
750
+ ...config,
751
+ preferences: {
752
+ ...config.preferences,
753
+ temperature: value
754
+ }
755
+ })
756
+ },
757
+ {
758
+ key: "autoSave",
759
+ label: "Auto Save",
760
+ type: "boolean",
761
+ getValue: (config) => config.preferences.autoSave,
762
+ setValue: (config, value) => ({
763
+ ...config,
764
+ preferences: {
765
+ ...config.preferences,
766
+ autoSave: value
767
+ }
768
+ })
769
+ },
770
+ {
771
+ key: "theme",
772
+ label: "Theme",
773
+ type: "select",
774
+ options: THEME_OPTIONS,
775
+ getValue: (config) => config.preferences.theme,
776
+ setValue: (config, value) => ({
777
+ ...config,
778
+ preferences: {
779
+ ...config.preferences,
780
+ theme: value
781
+ }
782
+ })
783
+ },
784
+ {
785
+ key: "exportFormat",
786
+ label: "Export Format",
787
+ type: "select",
788
+ options: EXPORT_FORMAT_OPTIONS,
789
+ getValue: (config) => config.preferences.exportFormat,
790
+ setValue: (config, value) => ({
791
+ ...config,
792
+ preferences: {
793
+ ...config.preferences,
794
+ exportFormat: value
795
+ }
796
+ })
797
+ }
798
+ );
799
+ return items;
800
+ }
801
+ function ConfigEditor({ config, availableModels, onSave, onClose }) {
778
802
  const [selectedIndex, setSelectedIndex] = useState2(0);
779
803
  const [editedConfig, setEditedConfig] = useState2(config);
780
804
  const [saveError, setSaveError] = useState2(null);
781
805
  const [isSaving, setIsSaving] = useState2(false);
806
+ const configItems = useMemo2(() => buildConfigItems(availableModels), [availableModels]);
782
807
  const hasChanges = useMemo2(() => {
783
- return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences);
784
- }, [config.preferences, editedConfig.preferences]);
808
+ return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
809
+ }, [config.preferences, editedConfig.preferences, config.defaultModel, editedConfig.defaultModel]);
785
810
  const handleSaveAndClose = async () => {
786
811
  if (hasChanges) {
787
812
  try {
@@ -798,13 +823,13 @@ function ConfigEditor({ config, onSave, onClose }) {
798
823
  onClose();
799
824
  };
800
825
  useInput2((input, key) => {
801
- const currentItem = CONFIG_ITEMS[selectedIndex];
826
+ const currentItem = configItems[selectedIndex];
802
827
  if (key.upArrow) {
803
- setSelectedIndex((prev) => prev > 0 ? prev - 1 : CONFIG_ITEMS.length - 1);
828
+ setSelectedIndex((prev) => prev > 0 ? prev - 1 : configItems.length - 1);
804
829
  return;
805
830
  }
806
831
  if (key.downArrow) {
807
- setSelectedIndex((prev) => prev < CONFIG_ITEMS.length - 1 ? prev + 1 : 0);
832
+ setSelectedIndex((prev) => prev < configItems.length - 1 ? prev + 1 : 0);
808
833
  return;
809
834
  }
810
835
  if ((key.escape || input === "q") && !isSaving) {
@@ -862,7 +887,7 @@ function ConfigEditor({ config, onSave, onClose }) {
862
887
  }
863
888
  return String(value);
864
889
  };
865
- 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) => {
890
+ 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) => {
866
891
  const isSelected = index === selectedIndex;
867
892
  const value = item.getValue(editedConfig);
868
893
  const displayValue = formatValue(item, value);
@@ -992,6 +1017,7 @@ function App({
992
1017
  onPermissionResponse,
993
1018
  commandHistory = [],
994
1019
  config,
1020
+ availableModels = [],
995
1021
  onSaveConfig
996
1022
  }) {
997
1023
  const messages = useCliStore((state) => state.session?.messages || []);
@@ -1033,7 +1059,15 @@ ${errorBlock}`;
1033
1059
  },
1034
1060
  [onMessage, onCommand, setIsThinking]
1035
1061
  );
1036
- 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(
1062
+ 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(
1063
+ ConfigEditor,
1064
+ {
1065
+ config,
1066
+ availableModels,
1067
+ onSave: onSaveConfig,
1068
+ onClose: () => setShowConfigEditor(false)
1069
+ }
1070
+ )) : permissionPrompt ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
1037
1071
  PermissionPrompt,
1038
1072
  {
1039
1073
  toolName: permissionPrompt.toolName,
@@ -1099,9 +1133,42 @@ var MessageList = React10.memo(
1099
1133
  }
1100
1134
  );
1101
1135
 
1102
- // src/components/LoginFlow.tsx
1103
- import React11, { useState as useState3, useEffect as useEffect2 } from "react";
1136
+ // src/components/TrustLocationSelector.tsx
1137
+ import React11 from "react";
1104
1138
  import { Box as Box11, Text as Text10 } from "ink";
1139
+ import SelectInput2 from "ink-select-input";
1140
+ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
1141
+ const items = [];
1142
+ if (inProject) {
1143
+ items.push({
1144
+ label: "Project settings (local) - Saved in .bike4mind/local.json (not committed)",
1145
+ value: "local"
1146
+ });
1147
+ items.push({
1148
+ label: "Project settings (team-wide) - Checked in at .bike4mind/config.json",
1149
+ value: "project"
1150
+ });
1151
+ }
1152
+ items.push({
1153
+ label: "User settings - Saved at ~/.bike4mind/config.json",
1154
+ value: "global"
1155
+ });
1156
+ const handleSelect = (item) => {
1157
+ onSelect(item.value);
1158
+ };
1159
+ 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(
1160
+ SelectInput2,
1161
+ {
1162
+ items,
1163
+ onSelect: handleSelect,
1164
+ itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
1165
+ }
1166
+ ), /* @__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")));
1167
+ }
1168
+
1169
+ // src/components/LoginFlow.tsx
1170
+ import React12, { useState as useState3, useEffect as useEffect2 } from "react";
1171
+ import { Box as Box12, Text as Text11 } from "ink";
1105
1172
  import Spinner2 from "ink-spinner";
1106
1173
  import jwt from "jsonwebtoken";
1107
1174
  import open from "open";
@@ -1264,15 +1331,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
1264
1331
  }
1265
1332
  }, [deviceFlow, status]);
1266
1333
  if (status === "initiating") {
1267
- return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan" }, /* @__PURE__ */ React11.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
1334
+ 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...")));
1268
1335
  }
1269
1336
  if (status === "error") {
1270
- return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { color: "red" }, error)));
1337
+ 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)));
1271
1338
  }
1272
1339
  if (status === "success") {
1273
- return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "You can now use B4M CLI with your account.")));
1340
+ 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.")));
1274
1341
  }
1275
- return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, null, "And enter this code when prompted:")), /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React11.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "cyan" }, /* @__PURE__ */ React11.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React11.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
1342
+ 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")));
1276
1343
  }
1277
1344
 
1278
1345
  // src/storage/SessionStore.ts
@@ -1394,7 +1461,7 @@ var SessionStore = class {
1394
1461
  };
1395
1462
 
1396
1463
  // src/storage/ConfigStore.ts
1397
- import { promises as fs4 } from "fs";
1464
+ import { promises as fs4, existsSync as existsSync2 } from "fs";
1398
1465
  import path4 from "path";
1399
1466
  import { homedir as homedir2 } from "os";
1400
1467
  import { v4 as uuidv4 } from "uuid";
@@ -1421,8 +1488,8 @@ var CliConfigSchema = z.object({
1421
1488
  mcpServers: z.array(
1422
1489
  z.object({
1423
1490
  name: z.string(),
1424
- command: z.string(),
1425
- args: z.array(z.string()),
1491
+ command: z.string().optional(),
1492
+ args: z.array(z.string()).optional(),
1426
1493
  env: z.record(z.string()),
1427
1494
  enabled: z.boolean()
1428
1495
  })
@@ -1442,6 +1509,53 @@ var CliConfigSchema = z.object({
1442
1509
  }),
1443
1510
  trustedTools: z.array(z.string()).optional().default([])
1444
1511
  });
1512
+ var ProjectConfigSchema = z.object({
1513
+ tools: z.object({
1514
+ enabled: z.array(z.string()).optional(),
1515
+ denied: z.array(z.string()).optional(),
1516
+ config: z.record(z.any()).optional()
1517
+ }).optional(),
1518
+ defaultModel: z.string().optional(),
1519
+ mcpServers: z.array(
1520
+ z.object({
1521
+ name: z.string(),
1522
+ command: z.string().optional(),
1523
+ args: z.array(z.string()).optional(),
1524
+ env: z.record(z.string()),
1525
+ enabled: z.boolean()
1526
+ })
1527
+ ).optional(),
1528
+ preferences: z.object({
1529
+ maxTokens: z.number().optional(),
1530
+ temperature: z.number().optional(),
1531
+ autoSave: z.boolean().optional(),
1532
+ theme: z.enum(["light", "dark"]).optional(),
1533
+ exportFormat: z.enum(["markdown", "json"]).optional()
1534
+ }).optional()
1535
+ });
1536
+ var ProjectLocalConfigSchema = z.object({
1537
+ trustedTools: z.array(z.string()).optional(),
1538
+ toolApiKeys: z.object({
1539
+ openweather: z.string().optional(),
1540
+ serper: z.string().optional()
1541
+ }).optional(),
1542
+ preferences: z.object({
1543
+ maxTokens: z.number().optional(),
1544
+ temperature: z.number().optional(),
1545
+ autoSave: z.boolean().optional(),
1546
+ theme: z.enum(["light", "dark"]).optional(),
1547
+ exportFormat: z.enum(["markdown", "json"]).optional()
1548
+ }).optional(),
1549
+ mcpServers: z.array(
1550
+ z.object({
1551
+ name: z.string(),
1552
+ command: z.string().optional(),
1553
+ args: z.array(z.string()).optional(),
1554
+ env: z.record(z.string()),
1555
+ enabled: z.boolean()
1556
+ })
1557
+ ).optional()
1558
+ });
1445
1559
  var DEFAULT_CONFIG = {
1446
1560
  version: "0.1.0",
1447
1561
  userId: uuidv4(),
@@ -1468,9 +1582,124 @@ var DEFAULT_CONFIG = {
1468
1582
  trustedTools: []
1469
1583
  // No tools trusted by default
1470
1584
  };
1585
+ function findProjectConfigDir(startDir = process.cwd()) {
1586
+ let currentDir = startDir;
1587
+ const { root } = path4.parse(currentDir);
1588
+ while (currentDir !== root) {
1589
+ const gitPath = path4.join(currentDir, ".git");
1590
+ try {
1591
+ if (existsSync2(gitPath)) {
1592
+ return currentDir;
1593
+ }
1594
+ } catch {
1595
+ }
1596
+ currentDir = path4.dirname(currentDir);
1597
+ }
1598
+ return process.cwd();
1599
+ }
1600
+ async function loadProjectConfig(projectDir) {
1601
+ const configPath = path4.join(projectDir, ".bike4mind", "config.json");
1602
+ try {
1603
+ const data = await fs4.readFile(configPath, "utf-8");
1604
+ const rawConfig = JSON.parse(data);
1605
+ const validated = ProjectConfigSchema.parse(rawConfig);
1606
+ return validated;
1607
+ } catch (error) {
1608
+ if (error.code === "ENOENT") {
1609
+ return null;
1610
+ }
1611
+ if (error instanceof z.ZodError) {
1612
+ console.error("Project config validation error:", error.errors);
1613
+ return null;
1614
+ }
1615
+ console.error("Failed to load project config:", error);
1616
+ return null;
1617
+ }
1618
+ }
1619
+ async function loadProjectLocalConfig(projectDir) {
1620
+ const configPath = path4.join(projectDir, ".bike4mind", "local.json");
1621
+ try {
1622
+ const data = await fs4.readFile(configPath, "utf-8");
1623
+ const rawConfig = JSON.parse(data);
1624
+ const validated = ProjectLocalConfigSchema.parse(rawConfig);
1625
+ return validated;
1626
+ } catch (error) {
1627
+ if (error.code === "ENOENT") {
1628
+ return null;
1629
+ }
1630
+ if (error instanceof z.ZodError) {
1631
+ console.error("Project local config validation error:", error.errors);
1632
+ return null;
1633
+ }
1634
+ console.error("Failed to load project local config:", error);
1635
+ return null;
1636
+ }
1637
+ }
1638
+ function mergeMcpServers(...serverArrays) {
1639
+ const serverMap = /* @__PURE__ */ new Map();
1640
+ for (const servers of serverArrays) {
1641
+ if (servers) {
1642
+ for (const server of servers) {
1643
+ serverMap.set(server.name, server);
1644
+ }
1645
+ }
1646
+ }
1647
+ return Array.from(serverMap.values());
1648
+ }
1649
+ function mergeConfigs(global, project, local) {
1650
+ const merged = { ...global };
1651
+ if (project) {
1652
+ if (project.defaultModel) {
1653
+ merged.defaultModel = project.defaultModel;
1654
+ }
1655
+ if (project.preferences) {
1656
+ merged.preferences = {
1657
+ ...merged.preferences,
1658
+ ...project.preferences
1659
+ };
1660
+ }
1661
+ if (project.tools) {
1662
+ merged.tools = {
1663
+ ...merged.tools,
1664
+ enabled: [...merged.tools.enabled || [], ...project.tools.enabled || []],
1665
+ disabled: [...merged.tools.disabled, ...project.tools.denied || []],
1666
+ config: {
1667
+ ...merged.tools.config,
1668
+ ...project.tools.config
1669
+ }
1670
+ };
1671
+ }
1672
+ if (project.mcpServers) {
1673
+ merged.mcpServers = mergeMcpServers(merged.mcpServers, project.mcpServers);
1674
+ }
1675
+ }
1676
+ if (local) {
1677
+ if (local.trustedTools) {
1678
+ const trustedSet = /* @__PURE__ */ new Set([...merged.trustedTools || [], ...local.trustedTools]);
1679
+ merged.trustedTools = Array.from(trustedSet);
1680
+ }
1681
+ if (local.toolApiKeys) {
1682
+ merged.toolApiKeys = {
1683
+ ...merged.toolApiKeys,
1684
+ ...local.toolApiKeys
1685
+ };
1686
+ }
1687
+ if (local.preferences) {
1688
+ merged.preferences = {
1689
+ ...merged.preferences,
1690
+ ...local.preferences
1691
+ };
1692
+ }
1693
+ if (local.mcpServers) {
1694
+ merged.mcpServers = mergeMcpServers(merged.mcpServers, local.mcpServers);
1695
+ }
1696
+ }
1697
+ return merged;
1698
+ }
1471
1699
  var ConfigStore = class {
1472
1700
  constructor(configPath) {
1473
1701
  this.config = null;
1702
+ this.projectConfigDir = null;
1474
1703
  this.configPath = configPath || path4.join(homedir2(), ".bike4mind", "config.json");
1475
1704
  }
1476
1705
  /**
@@ -1487,51 +1716,76 @@ var ConfigStore = class {
1487
1716
  }
1488
1717
  /**
1489
1718
  * Load configuration from disk with Zod validation
1719
+ * Merges global → project → local configs
1490
1720
  */
1491
1721
  async load() {
1492
1722
  if (this.config) {
1493
1723
  return this.config;
1494
1724
  }
1495
1725
  try {
1726
+ let globalConfig;
1496
1727
  try {
1497
- const stats = await fs4.stat(this.configPath);
1498
- const mode = stats.mode & 511;
1499
- if (mode !== 384) {
1500
- console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
1501
- await fs4.chmod(this.configPath, 384);
1728
+ try {
1729
+ const stats = await fs4.stat(this.configPath);
1730
+ const mode = stats.mode & 511;
1731
+ if (mode !== 384) {
1732
+ console.warn(`\u26A0\uFE0F Config file has insecure permissions (${mode.toString(8)}). Setting to 0600...`);
1733
+ await fs4.chmod(this.configPath, 384);
1734
+ }
1735
+ } catch (statError) {
1736
+ }
1737
+ const data = await fs4.readFile(this.configPath, "utf-8");
1738
+ const rawConfig = JSON.parse(data);
1739
+ if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
1740
+ const oldApiConfig = rawConfig.apiConfig;
1741
+ if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) {
1742
+ rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
1743
+ } else {
1744
+ rawConfig.apiConfig = {};
1745
+ }
1502
1746
  }
1503
- } catch (statError) {
1504
- }
1505
- const data = await fs4.readFile(this.configPath, "utf-8");
1506
- const rawConfig = JSON.parse(data);
1507
- if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
1508
- const oldApiConfig = rawConfig.apiConfig;
1509
- if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) {
1510
- rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
1747
+ const validated = CliConfigSchema.parse(rawConfig);
1748
+ globalConfig = {
1749
+ ...DEFAULT_CONFIG,
1750
+ ...validated,
1751
+ auth: validated.auth,
1752
+ // Explicitly preserve auth field
1753
+ preferences: {
1754
+ ...DEFAULT_CONFIG.preferences,
1755
+ ...validated.preferences
1756
+ },
1757
+ tools: {
1758
+ ...DEFAULT_CONFIG.tools,
1759
+ ...validated.tools
1760
+ },
1761
+ toolApiKeys: {
1762
+ ...DEFAULT_CONFIG.toolApiKeys,
1763
+ ...validated.toolApiKeys || {}
1764
+ },
1765
+ trustedTools: validated.trustedTools || []
1766
+ };
1767
+ } catch (error) {
1768
+ if (error.code === "ENOENT") {
1769
+ globalConfig = { ...DEFAULT_CONFIG };
1770
+ } else if (error instanceof z.ZodError) {
1771
+ console.error("Global config validation error:", error.errors);
1772
+ console.error("Using default configuration");
1773
+ globalConfig = { ...DEFAULT_CONFIG };
1511
1774
  } else {
1512
- rawConfig.apiConfig = {};
1775
+ throw error;
1513
1776
  }
1514
1777
  }
1515
- const validated = CliConfigSchema.parse(rawConfig);
1516
- this.config = {
1517
- ...DEFAULT_CONFIG,
1518
- ...validated,
1519
- auth: validated.auth,
1520
- // Explicitly preserve auth field
1521
- preferences: {
1522
- ...DEFAULT_CONFIG.preferences,
1523
- ...validated.preferences
1524
- },
1525
- tools: {
1526
- ...DEFAULT_CONFIG.tools,
1527
- ...validated.tools
1528
- },
1529
- toolApiKeys: {
1530
- ...DEFAULT_CONFIG.toolApiKeys,
1531
- ...validated.toolApiKeys || {}
1532
- },
1533
- trustedTools: validated.trustedTools || []
1534
- };
1778
+ this.projectConfigDir = findProjectConfigDir();
1779
+ let projectConfig = null;
1780
+ let projectLocalConfig = null;
1781
+ if (this.projectConfigDir) {
1782
+ projectConfig = await loadProjectConfig(this.projectConfigDir);
1783
+ projectLocalConfig = await loadProjectLocalConfig(this.projectConfigDir);
1784
+ if (projectConfig) {
1785
+ console.log(`\u{1F4C1} Project config loaded from: ${this.projectConfigDir}/.bike4mind/`);
1786
+ }
1787
+ }
1788
+ this.config = mergeConfigs(globalConfig, projectConfig, projectLocalConfig);
1535
1789
  return this.config;
1536
1790
  } catch (error) {
1537
1791
  if (error.code === "ENOENT") {
@@ -1720,6 +1974,92 @@ var ConfigStore = class {
1720
1974
  }
1721
1975
  await this.save(config);
1722
1976
  }
1977
+ /**
1978
+ * Get project config directory (if any)
1979
+ */
1980
+ getProjectConfigDir() {
1981
+ return this.projectConfigDir;
1982
+ }
1983
+ /**
1984
+ * Initialize project config directory
1985
+ * Creates .bike4mind/ directory and ensures local.json is gitignored
1986
+ * Does NOT auto-create config.json (user creates that manually)
1987
+ */
1988
+ async initProjectConfig() {
1989
+ const projectDir = this.projectConfigDir || findProjectConfigDir();
1990
+ if (!projectDir) {
1991
+ return;
1992
+ }
1993
+ const configDir = path4.join(projectDir, ".bike4mind");
1994
+ await fs4.mkdir(configDir, { recursive: true });
1995
+ await this.ensureGitignore(projectDir);
1996
+ }
1997
+ /**
1998
+ * Ensure .gitignore includes .bike4mind/local.json
1999
+ */
2000
+ async ensureGitignore(projectDir) {
2001
+ const gitignorePath = path4.join(projectDir, ".gitignore");
2002
+ const entryToAdd = ".bike4mind/local.json";
2003
+ try {
2004
+ let gitignoreContent = "";
2005
+ try {
2006
+ gitignoreContent = await fs4.readFile(gitignorePath, "utf-8");
2007
+ } catch {
2008
+ }
2009
+ if (gitignoreContent.includes(entryToAdd)) {
2010
+ return;
2011
+ }
2012
+ const newContent = gitignoreContent.trim() + (gitignoreContent ? "\n" : "") + `
2013
+ # Bike4Mind local config (developer-specific)
2014
+ ${entryToAdd}
2015
+ `;
2016
+ await fs4.writeFile(gitignorePath, newContent, "utf-8");
2017
+ console.log(`\u2705 Added ${entryToAdd} to .gitignore`);
2018
+ } catch (error) {
2019
+ console.warn(`\u26A0\uFE0F Failed to update .gitignore:`, error);
2020
+ }
2021
+ }
2022
+ /**
2023
+ * Save project config to .bike4mind/config.json
2024
+ */
2025
+ async saveProjectConfig(config, projectDir) {
2026
+ const targetDir = projectDir || this.projectConfigDir || process.cwd();
2027
+ const configPath = path4.join(targetDir, ".bike4mind", "config.json");
2028
+ await fs4.mkdir(path4.dirname(configPath), { recursive: true });
2029
+ const validated = ProjectConfigSchema.parse(config);
2030
+ await fs4.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
2031
+ console.log(`\u2705 Saved project config to: ${configPath}`);
2032
+ }
2033
+ /**
2034
+ * Save project-local config to .bike4mind/local.json
2035
+ */
2036
+ async saveProjectLocalConfig(config, projectDir) {
2037
+ const targetDir = projectDir || this.projectConfigDir || process.cwd();
2038
+ const configPath = path4.join(targetDir, ".bike4mind", "local.json");
2039
+ await fs4.mkdir(path4.dirname(configPath), { recursive: true });
2040
+ const validated = ProjectLocalConfigSchema.parse(config);
2041
+ await fs4.writeFile(configPath, JSON.stringify(validated, null, 2), "utf-8");
2042
+ await fs4.chmod(configPath, 384);
2043
+ console.log(`\u2705 Saved project-local config to: ${configPath}`);
2044
+ }
2045
+ /**
2046
+ * Load raw project config (without merging)
2047
+ */
2048
+ async loadRawProjectConfig() {
2049
+ if (!this.projectConfigDir) {
2050
+ return null;
2051
+ }
2052
+ return loadProjectConfig(this.projectConfigDir);
2053
+ }
2054
+ /**
2055
+ * Load raw project-local config (without merging)
2056
+ */
2057
+ async loadRawProjectLocalConfig() {
2058
+ if (!this.projectConfigDir) {
2059
+ return null;
2060
+ }
2061
+ return loadProjectLocalConfig(this.projectConfigDir);
2062
+ }
1723
2063
  };
1724
2064
 
1725
2065
  // src/storage/CommandHistoryStore.ts
@@ -4471,13 +4811,30 @@ async function performWebSearch(adapters, params) {
4471
4811
  link: result.link,
4472
4812
  snippet: result.snippet
4473
4813
  })) || [];
4814
+ const citables = results.map((result, index) => ({
4815
+ id: result.link,
4816
+ // Use URL as unique identifier
4817
+ type: "web_url",
4818
+ title: result.title,
4819
+ url: result.link,
4820
+ description: result.snippet,
4821
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4822
+ status: "complete",
4823
+ metadata: {
4824
+ sourceSystem: "web_search",
4825
+ relevanceScore: 1 - index * 0.1,
4826
+ // Higher relevance for earlier results
4827
+ fullContext: result.snippet
4828
+ }
4829
+ }));
4474
4830
  const formattedResults = results.map((result, index) => `${index + 1}. **${result.title}**
4475
4831
  ${result.snippet}
4476
4832
  Source: [${new URL(result.link).hostname}](${result.link})
4477
4833
  `).join("\n");
4478
- return formattedResults ? `Here's what I found from searching the web:
4834
+ const formattedOutput = formattedResults ? `Here's what I found from searching the web:
4479
4835
 
4480
4836
  ${formattedResults}` : "No results found from web search.";
4837
+ return { formattedResults: formattedOutput, citables };
4481
4838
  } catch (error) {
4482
4839
  console.error("\u274C WebSearch Tool: Error during search:", error);
4483
4840
  throw error;
@@ -4488,7 +4845,18 @@ var webSearchTool = {
4488
4845
  implementation: (context) => ({
4489
4846
  toolFn: async (value) => {
4490
4847
  const params = value;
4491
- return performWebSearch({ db: context.db }, params);
4848
+ const { formattedResults, citables } = await performWebSearch({ db: context.db }, params);
4849
+ if (citables.length > 0) {
4850
+ await context.statusUpdate({
4851
+ // Store citables in promptMeta - statusUpdate will Object.assign this
4852
+ // The receiving code needs to handle merging promptMeta.citables
4853
+ promptMeta: {
4854
+ citables
4855
+ }
4856
+ }, "Web search complete");
4857
+ console.log(`\u{1F4DA} WebSearch Tool: Stored ${citables.length} citables`);
4858
+ }
4859
+ return formattedResults;
4492
4860
  },
4493
4861
  toolSchema: {
4494
4862
  name: "web_search",
@@ -7305,7 +7673,7 @@ var planetVisibilityTool = {
7305
7673
 
7306
7674
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
7307
7675
  import { promises as fs7 } from "fs";
7308
- import { existsSync as existsSync2, statSync as statSync3 } from "fs";
7676
+ import { existsSync as existsSync3, statSync as statSync3 } from "fs";
7309
7677
  import path6 from "path";
7310
7678
  var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
7311
7679
  async function readFileContent(params) {
@@ -7316,7 +7684,7 @@ async function readFileContent(params) {
7316
7684
  if (!resolvedPath.startsWith(cwd)) {
7317
7685
  throw new Error(`Access denied: Cannot read files outside of current working directory`);
7318
7686
  }
7319
- if (!existsSync2(resolvedPath)) {
7687
+ if (!existsSync3(resolvedPath)) {
7320
7688
  throw new Error(`File not found: ${filePath}`);
7321
7689
  }
7322
7690
  const stats = statSync3(resolvedPath);
@@ -7402,7 +7770,7 @@ var fileReadTool = {
7402
7770
 
7403
7771
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
7404
7772
  import { promises as fs8 } from "fs";
7405
- import { existsSync as existsSync3 } from "fs";
7773
+ import { existsSync as existsSync4 } from "fs";
7406
7774
  import path7 from "path";
7407
7775
  async function createFile(params) {
7408
7776
  const { path: filePath, content, createDirectories = true } = params;
@@ -7412,7 +7780,7 @@ async function createFile(params) {
7412
7780
  if (!resolvedPath.startsWith(cwd)) {
7413
7781
  throw new Error(`Access denied: Cannot create files outside of current working directory`);
7414
7782
  }
7415
- const fileExists = existsSync3(resolvedPath);
7783
+ const fileExists = existsSync4(resolvedPath);
7416
7784
  const action = fileExists ? "overwritten" : "created";
7417
7785
  if (createDirectories) {
7418
7786
  const dir = path7.dirname(resolvedPath);
@@ -7430,7 +7798,7 @@ var createFileTool = {
7430
7798
  implementation: (context) => ({
7431
7799
  toolFn: async (value) => {
7432
7800
  const params = value;
7433
- const fileExists = existsSync3(path7.resolve(process.cwd(), path7.normalize(params.path)));
7801
+ const fileExists = existsSync4(path7.resolve(process.cwd(), path7.normalize(params.path)));
7434
7802
  context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
7435
7803
  path: params.path,
7436
7804
  size: params.content.length
@@ -7810,7 +8178,7 @@ var grepSearchTool = {
7810
8178
 
7811
8179
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
7812
8180
  import { promises as fs9 } from "fs";
7813
- import { existsSync as existsSync4, statSync as statSync6 } from "fs";
8181
+ import { existsSync as existsSync5, statSync as statSync6 } from "fs";
7814
8182
  import path10 from "path";
7815
8183
  async function deleteFile(params) {
7816
8184
  const { path: filePath, recursive = false } = params;
@@ -7820,7 +8188,7 @@ async function deleteFile(params) {
7820
8188
  if (!resolvedPath.startsWith(cwd)) {
7821
8189
  throw new Error(`Access denied: Cannot delete files outside of current working directory`);
7822
8190
  }
7823
- if (!existsSync4(resolvedPath)) {
8191
+ if (!existsSync5(resolvedPath)) {
7824
8192
  throw new Error(`File or directory not found: ${filePath}`);
7825
8193
  }
7826
8194
  const stats = statSync6(resolvedPath);
@@ -7844,7 +8212,7 @@ var deleteFileTool = {
7844
8212
  toolFn: async (value) => {
7845
8213
  const params = value;
7846
8214
  const resolvedPath = path10.resolve(process.cwd(), path10.normalize(params.path));
7847
- const isDirectory = existsSync4(resolvedPath) && statSync6(resolvedPath).isDirectory();
8215
+ const isDirectory = existsSync5(resolvedPath) && statSync6(resolvedPath).isDirectory();
7848
8216
  context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
7849
8217
  path: params.path,
7850
8218
  recursive: params.recursive
@@ -8134,10 +8502,10 @@ import jwt2 from "jsonwebtoken";
8134
8502
  // src/utils/diffPreview.ts
8135
8503
  import * as Diff from "diff";
8136
8504
  import { readFile } from "fs/promises";
8137
- import { existsSync as existsSync5 } from "fs";
8505
+ import { existsSync as existsSync6 } from "fs";
8138
8506
  async function generateFileDiffPreview(args) {
8139
8507
  try {
8140
- if (!existsSync5(args.path)) {
8508
+ if (!existsSync6(args.path)) {
8141
8509
  const lines2 = args.content.split("\n");
8142
8510
  const preview = lines2.slice(0, 20).join("\n");
8143
8511
  const hasMore = lines2.length > 20;
@@ -8167,7 +8535,7 @@ ${preview}${hasMore ? `
8167
8535
  }
8168
8536
  async function generateFileDeletePreview(args) {
8169
8537
  try {
8170
- if (!existsSync5(args.path)) {
8538
+ if (!existsSync6(args.path)) {
8171
8539
  return `[File does not exist: ${args.path}]`;
8172
8540
  }
8173
8541
  const stats = await import("fs/promises").then((fs11) => fs11.stat(args.path));
@@ -8220,7 +8588,7 @@ var CliLogger = class extends Logger {
8220
8588
  log(...args) {
8221
8589
  }
8222
8590
  };
8223
- function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext) {
8591
+ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore) {
8224
8592
  const originalFn = tool.toolFn;
8225
8593
  const toolName = tool.toolSchema.name;
8226
8594
  return {
@@ -8264,7 +8632,25 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
8264
8632
  throw new PermissionDeniedError(toolName, args);
8265
8633
  }
8266
8634
  if (response.action === "allow-always") {
8267
- permissionManager.trustTool(toolName);
8635
+ const canTrust = permissionManager.trustTool(toolName);
8636
+ if (canTrust) {
8637
+ const projectDir = configStore.getProjectConfigDir();
8638
+ if (projectDir) {
8639
+ try {
8640
+ await configStore.initProjectConfig();
8641
+ const existingLocal = await configStore.loadRawProjectLocalConfig() || {};
8642
+ const updatedLocal = {
8643
+ ...existingLocal,
8644
+ trustedTools: [...existingLocal.trustedTools || [], toolName]
8645
+ };
8646
+ await configStore.saveProjectLocalConfig(updatedLocal);
8647
+ } catch (error) {
8648
+ await configStore.trustTool(toolName);
8649
+ }
8650
+ } else {
8651
+ await configStore.trustTool(toolName);
8652
+ }
8653
+ }
8268
8654
  }
8269
8655
  const result = await originalFn(args);
8270
8656
  agentContext.observationQueue.push({ toolName, result });
@@ -8272,7 +8658,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
8272
8658
  }
8273
8659
  };
8274
8660
  }
8275
- function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, toolApiKeys) {
8661
+ function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, toolApiKeys) {
8276
8662
  const logger2 = new CliLogger();
8277
8663
  const storage = new NoOpStorage();
8278
8664
  const user = {
@@ -8356,7 +8742,9 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
8356
8742
  toolConfig,
8357
8743
  model
8358
8744
  );
8359
- const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext));
8745
+ const tools2 = Object.entries(toolsMap).filter(([key]) => toolConfig[key] !== void 0).map(
8746
+ ([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore)
8747
+ );
8360
8748
  return { tools: tools2, agentContext };
8361
8749
  }
8362
8750
 
@@ -8421,23 +8809,30 @@ function canTrustTool(toolName, customCategories) {
8421
8809
 
8422
8810
  // src/utils/PermissionManager.ts
8423
8811
  var PermissionManager = class {
8424
- constructor(trustedTools = [], customCategories) {
8812
+ constructor(trustedTools = [], customCategories, deniedTools) {
8425
8813
  this.trustedTools = /* @__PURE__ */ new Set();
8814
+ this.deniedTools = /* @__PURE__ */ new Set();
8426
8815
  this.trustedTools = new Set(trustedTools);
8427
8816
  this.customCategories = new Map(Object.entries(customCategories || {}));
8817
+ this.deniedTools = new Set(deniedTools || []);
8428
8818
  }
8429
8819
  /**
8430
8820
  * Check if a tool needs permission before execution
8431
8821
  *
8432
8822
  * A tool needs permission if:
8433
- * 1. It's not in the auto_approve category, AND
8434
- * 2. It's not in the trusted tools set
8823
+ * 1. It's in the denied tools list (from project config), OR
8824
+ * 2. It's not in the auto_approve category, AND
8825
+ * 3. It's not in the trusted tools set
8435
8826
  *
8436
8827
  * Note: prompt_always tools CANNOT be trusted, so they always need permission
8828
+ * Note: denied tools from project config ALWAYS need permission (cannot be overridden locally)
8437
8829
  */
8438
8830
  needsPermission(toolName) {
8439
8831
  const categoryMap = Object.fromEntries(this.customCategories);
8440
8832
  const category = getToolCategory(toolName, categoryMap);
8833
+ if (this.deniedTools.has(toolName)) {
8834
+ return true;
8835
+ }
8441
8836
  if (category === "auto_approve") {
8442
8837
  return false;
8443
8838
  }
@@ -8448,10 +8843,13 @@ var PermissionManager = class {
8448
8843
  }
8449
8844
  /**
8450
8845
  * Add a tool to the trusted list
8451
- * Returns false if the tool cannot be trusted (prompt_always category)
8846
+ * Returns false if the tool cannot be trusted (prompt_always category or denied by project)
8452
8847
  */
8453
8848
  trustTool(toolName) {
8454
8849
  const categoryMap = Object.fromEntries(this.customCategories);
8850
+ if (this.deniedTools.has(toolName)) {
8851
+ return false;
8852
+ }
8455
8853
  if (!canTrustTool(toolName, categoryMap)) {
8456
8854
  return false;
8457
8855
  }
@@ -8484,12 +8882,27 @@ var PermissionManager = class {
8484
8882
  return getToolCategory(toolName, categoryMap);
8485
8883
  }
8486
8884
  /**
8487
- * Check if a tool can be trusted (not in prompt_always category)
8885
+ * Check if a tool can be trusted (not in prompt_always category or denied by project)
8488
8886
  */
8489
8887
  canBeTrusted(toolName) {
8490
8888
  const categoryMap = Object.fromEntries(this.customCategories);
8889
+ if (this.deniedTools.has(toolName)) {
8890
+ return false;
8891
+ }
8491
8892
  return canTrustTool(toolName, categoryMap);
8492
8893
  }
8894
+ /**
8895
+ * Check if a tool is denied by project config
8896
+ */
8897
+ isDenied(toolName) {
8898
+ return this.deniedTools.has(toolName);
8899
+ }
8900
+ /**
8901
+ * Get list of denied tools
8902
+ */
8903
+ getDeniedTools() {
8904
+ return Array.from(this.deniedTools).sort();
8905
+ }
8493
8906
  /**
8494
8907
  * Clear all trusted tools
8495
8908
  */
@@ -8784,18 +9197,22 @@ var logger = Logger2.getInstance();
8784
9197
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
8785
9198
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
8786
9199
  import path12 from "path";
8787
- import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
9200
+ import { existsSync as existsSync7, readdirSync as readdirSync3 } from "fs";
8788
9201
  var MCPClient = class {
8789
9202
  // Note: This class handles MCP server communication with repository filtering
8790
9203
  mcp;
8791
9204
  transport = null;
8792
9205
  envVariables;
9206
+ customCommand;
9207
+ customArgs;
8793
9208
  tools = [];
8794
9209
  serverName;
8795
- constructor({ envVariables, name, selectedRepositories }) {
9210
+ constructor({ envVariables, name, selectedRepositories, command, args }) {
8796
9211
  this.mcp = new Client2({ name: "mcp-client-cli", version: "1.0.0" });
8797
9212
  this.envVariables = [...envVariables];
8798
9213
  this.serverName = name;
9214
+ this.customCommand = command;
9215
+ this.customArgs = args;
8799
9216
  if (name === "github" && selectedRepositories) {
8800
9217
  const reposJson = JSON.stringify(selectedRepositories);
8801
9218
  this.envVariables.push({
@@ -8806,37 +9223,48 @@ var MCPClient = class {
8806
9223
  }
8807
9224
  async connectToServer() {
8808
9225
  try {
8809
- const root = process.env.INIT_CWD || process.cwd();
8810
- const candidatePaths = [
8811
- // When running from SST Lambda with node_modules structure (copyFiles)
8812
- path12.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
8813
- // When running from SST Lambda deployed environment (/var/task)
8814
- path12.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8815
- // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
8816
- path12.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8817
- // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
8818
- path12.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8819
- // Original paths (backward compatibility)
8820
- path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8821
- path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
8822
- ];
8823
- const serverScriptPath = candidatePaths.find((p) => existsSync6(p));
8824
- if (!serverScriptPath) {
8825
- const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
8826
- console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
8827
- console.error(`[MCP] Available directories in ${root}:`, getDirectories(root));
8828
- throw new Error(`MCP Server script path not found for ${this.serverName}. Root: ${root}, Available dirs: ${getDirectories(root).join(", ")}`);
8829
- }
8830
- console.log(`[MCP] Using server: ${this.serverName} at ${serverScriptPath}`);
9226
+ const envVarsObject = this.envVariables.reduce((acc, env) => ({
9227
+ ...acc,
9228
+ [env.key]: env.value
9229
+ }), {});
9230
+ let command;
9231
+ let args;
9232
+ if (this.customCommand && this.customCommand.trim() !== "") {
9233
+ command = this.customCommand;
9234
+ args = this.customArgs ?? [];
9235
+ console.log(`[MCP] Using external command for ${this.serverName}: ${command} ${args.join(" ")}`);
9236
+ } else {
9237
+ const root = process.env.INIT_CWD || process.cwd();
9238
+ const candidatePaths = [
9239
+ // When running from SST Lambda with node_modules structure (copyFiles)
9240
+ path12.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
9241
+ // When running from SST Lambda deployed environment (/var/task)
9242
+ path12.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9243
+ // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
9244
+ path12.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9245
+ // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
9246
+ path12.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9247
+ // Original paths (backward compatibility)
9248
+ path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9249
+ path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
9250
+ ];
9251
+ const serverScriptPath = candidatePaths.find((p) => existsSync7(p));
9252
+ if (!serverScriptPath) {
9253
+ const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
9254
+ console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
9255
+ console.error(`[MCP] Available directories in ${root}:`, getDirectories(root));
9256
+ throw new Error(`MCP Server script path not found for ${this.serverName}. Root: ${root}, Available dirs: ${getDirectories(root).join(", ")}`);
9257
+ }
9258
+ command = process.execPath;
9259
+ args = [serverScriptPath];
9260
+ console.log(`[MCP] Using server: ${this.serverName} at ${serverScriptPath}`);
9261
+ }
8831
9262
  this.transport = new StdioClientTransport({
8832
- command: process.execPath,
8833
- args: [serverScriptPath],
9263
+ command,
9264
+ args,
8834
9265
  env: {
8835
- ...process.env,
8836
- ...this.envVariables.reduce((acc, env) => ({
8837
- ...acc,
8838
- [env.key]: env.value
8839
- }), {})
9266
+ ...Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== void 0)),
9267
+ ...envVarsObject
8840
9268
  }
8841
9269
  });
8842
9270
  this.transport.onerror = (error) => {
@@ -8944,7 +9372,9 @@ var McpManager = class {
8944
9372
  }));
8945
9373
  const client = new MCPClient({
8946
9374
  envVariables,
8947
- name: serverConfig.name
9375
+ name: serverConfig.name,
9376
+ command: serverConfig.command,
9377
+ args: serverConfig.args
8948
9378
  });
8949
9379
  await client.connectToServer();
8950
9380
  const mcpData = {
@@ -9215,9 +9645,40 @@ var ServerLlmBackend = class {
9215
9645
  }
9216
9646
  /**
9217
9647
  * Get available models from server
9218
- * For now, returns hardcoded list of supported models
9648
+ * Fetches from /api/models and filters for CLI-compatible models
9649
+ * Falls back to hardcoded list if API fails
9219
9650
  */
9220
9651
  async getModelInfo() {
9652
+ try {
9653
+ logger.debug("[ServerLlmBackend] Fetching models from /api/models");
9654
+ const response = await this.apiClient.get("/api/models");
9655
+ if (!response || typeof response !== "object" || !Array.isArray(response.models)) {
9656
+ logger.warn("[ServerLlmBackend] Invalid API response format, using fallback models");
9657
+ logger.info("\u26A0\uFE0F Using fallback model list (API returned invalid format)");
9658
+ return this.getFallbackModels();
9659
+ }
9660
+ const filteredModels = response.models.filter((model) => model.type === "text" && model.supportsTools === true);
9661
+ logger.debug(`[ServerLlmBackend] Fetched ${filteredModels.length} CLI-compatible models`);
9662
+ if (filteredModels.length === 0) {
9663
+ logger.warn("[ServerLlmBackend] No CLI-compatible models found from API, using fallback");
9664
+ logger.info("\u26A0\uFE0F Using fallback model list (no CLI-compatible models available)");
9665
+ return this.getFallbackModels();
9666
+ }
9667
+ logger.info(`\u{1F4CB} Loaded ${filteredModels.length} models from server`);
9668
+ return filteredModels;
9669
+ } catch (error) {
9670
+ logger.warn(
9671
+ `[ServerLlmBackend] Failed to fetch models from API, using fallback: ${error instanceof Error ? error.message : String(error)}`
9672
+ );
9673
+ logger.info("\u26A0\uFE0F Using fallback model list (API unavailable)");
9674
+ return this.getFallbackModels();
9675
+ }
9676
+ }
9677
+ /**
9678
+ * Fallback models when API is unavailable
9679
+ * Returns hardcoded list of commonly supported models
9680
+ */
9681
+ getFallbackModels() {
9221
9682
  return [
9222
9683
  {
9223
9684
  id: "claude-sonnet-4-5-20250929",
@@ -9402,7 +9863,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
9402
9863
  // package.json
9403
9864
  var package_default = {
9404
9865
  name: "@bike4mind/cli",
9405
- version: "0.2.5-fix-github-issue-type.17095+7ae053c6e",
9866
+ version: "0.2.6-fix-cli-mcp-type-mismatch.17119+0906e1e34",
9406
9867
  type: "module",
9407
9868
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
9408
9869
  license: "UNLICENSED",
@@ -9504,10 +9965,10 @@ var package_default = {
9504
9965
  },
9505
9966
  devDependencies: {
9506
9967
  "@bike4mind/agents": "0.1.0",
9507
- "@bike4mind/common": "2.37.1-fix-github-issue-type.17095+7ae053c6e",
9508
- "@bike4mind/mcp": "1.19.6-fix-github-issue-type.17095+7ae053c6e",
9509
- "@bike4mind/services": "2.32.2-fix-github-issue-type.17095+7ae053c6e",
9510
- "@bike4mind/utils": "2.1.1-fix-github-issue-type.17095+7ae053c6e",
9968
+ "@bike4mind/common": "2.38.1-fix-cli-mcp-type-mismatch.17119+0906e1e34",
9969
+ "@bike4mind/mcp": "1.20.1-fix-cli-mcp-type-mismatch.17119+0906e1e34",
9970
+ "@bike4mind/services": "2.33.1-fix-cli-mcp-type-mismatch.17119+0906e1e34",
9971
+ "@bike4mind/utils": "2.1.2-fix-cli-mcp-type-mismatch.17119+0906e1e34",
9511
9972
  "@types/diff": "^5.0.9",
9512
9973
  "@types/jsonwebtoken": "^9.0.4",
9513
9974
  "@types/node": "^22.9.0",
@@ -9519,7 +9980,7 @@ var package_default = {
9519
9980
  typescript: "^5.9.3",
9520
9981
  vitest: "^3.2.4"
9521
9982
  },
9522
- gitHead: "7ae053c6e896d08aa21ed44cdf3502ba04ae3cb7"
9983
+ gitHead: "0906e1e34709a52074b386d9b51a94776db1d33d"
9523
9984
  };
9524
9985
 
9525
9986
  // src/config/constants.ts
@@ -9548,7 +10009,8 @@ function CliApp() {
9548
10009
  agent: null,
9549
10010
  mcpManager: null,
9550
10011
  permissionManager: null,
9551
- permissionPrompt: null
10012
+ permissionPrompt: null,
10013
+ trustLocationSelector: null
9552
10014
  });
9553
10015
  const [isInitialized, setIsInitialized] = useState4(false);
9554
10016
  const [initError, setInitError] = useState4(null);
@@ -9710,7 +10172,13 @@ function CliApp() {
9710
10172
  debug: () => {
9711
10173
  }
9712
10174
  };
9713
- const permissionManager = new PermissionManager(config.trustedTools || []);
10175
+ const permissionManager = new PermissionManager(
10176
+ config.trustedTools || [],
10177
+ void 0,
10178
+ // customCategories (not used yet)
10179
+ config.tools.disabled || []
10180
+ // denied tools from project config
10181
+ );
9714
10182
  const promptFn = (toolName, args, preview) => {
9715
10183
  return new Promise((resolve3) => {
9716
10184
  const canBeTrusted = permissionManager.canBeTrusted(toolName);
@@ -9738,6 +10206,7 @@ function CliApp() {
9738
10206
  permissionManager,
9739
10207
  promptFn,
9740
10208
  agentContext,
10209
+ state.configStore,
9741
10210
  config.toolApiKeys
9742
10211
  );
9743
10212
  console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
@@ -9802,8 +10271,10 @@ Remember: Use context from previous messages to understand follow-up questions.`
9802
10271
  agent,
9803
10272
  mcpManager,
9804
10273
  permissionManager,
9805
- config
10274
+ config,
9806
10275
  // Store config for synchronous access
10276
+ availableModels: models
10277
+ // Store models for ConfigEditor
9807
10278
  }));
9808
10279
  setStoreSession(newSession);
9809
10280
  setIsInitialized(true);
@@ -9919,6 +10390,9 @@ Tool Permissions:
9919
10390
  /trust <tool-name> - Trust a tool (won't ask permission again)
9920
10391
  /untrust <tool-name> - Remove tool from trusted list
9921
10392
  /trusted - List all trusted tools
10393
+
10394
+ Project Configuration:
10395
+ /project-config - Show merged project configuration
9922
10396
  `);
9923
10397
  break;
9924
10398
  case "exit":
@@ -9997,7 +10471,7 @@ Tool Permissions:
9997
10471
  console.log("");
9998
10472
  break;
9999
10473
  }
10000
- case "trust":
10474
+ case "trust": {
10001
10475
  if (!state.permissionManager) {
10002
10476
  console.log("Permission manager not initialized");
10003
10477
  return;
@@ -10005,16 +10479,97 @@ Tool Permissions:
10005
10479
  const toolToTrust = args[0];
10006
10480
  if (!toolToTrust) {
10007
10481
  console.log("Usage: /trust <tool-name>");
10482
+ console.log("");
10483
+ console.log("Example:");
10484
+ console.log(" /trust file_read - Opens interactive location selector");
10485
+ console.log("");
10008
10486
  return;
10009
10487
  }
10010
- const trusted = state.permissionManager.trustTool(toolToTrust);
10011
- if (trusted) {
10012
- await state.configStore.trustTool(toolToTrust);
10013
- console.log(`\u2705 Tool '${toolToTrust}' is now trusted and won't require permission`);
10014
- } else {
10015
- console.log(`\u274C Tool '${toolToTrust}' cannot be trusted (it's in the 'prompt_always' category)`);
10488
+ const canTrust = state.permissionManager.trustTool(toolToTrust);
10489
+ if (!canTrust) {
10490
+ if (state.permissionManager.isDenied(toolToTrust)) {
10491
+ console.log(`\u274C Tool '${toolToTrust}' is denied by project configuration and cannot be trusted`);
10492
+ } else {
10493
+ console.log(`\u274C Tool '${toolToTrust}' cannot be trusted (it's in the 'prompt_always' category)`);
10494
+ }
10495
+ return;
10016
10496
  }
10497
+ state.permissionManager.untrustTool(toolToTrust);
10498
+ const handleLocationSelect = async (location) => {
10499
+ setState((prev) => ({ ...prev, trustLocationSelector: null }));
10500
+ if (!location) {
10501
+ console.log("\u274C Trust operation cancelled");
10502
+ return;
10503
+ }
10504
+ const projectDir = state.configStore.getProjectConfigDir();
10505
+ switch (location) {
10506
+ case "local": {
10507
+ if (!projectDir) {
10508
+ console.log('\u274C No project found. Use "global" to save to ~/.bike4mind/config.json');
10509
+ return;
10510
+ }
10511
+ try {
10512
+ await state.configStore.initProjectConfig();
10513
+ const existingLocal = await state.configStore.loadRawProjectLocalConfig() || {};
10514
+ const updatedLocal = {
10515
+ ...existingLocal,
10516
+ trustedTools: [.../* @__PURE__ */ new Set([...existingLocal.trustedTools || [], toolToTrust])]
10517
+ };
10518
+ await state.configStore.saveProjectLocalConfig(updatedLocal);
10519
+ state.permissionManager?.trustTool(toolToTrust);
10520
+ console.log(`\u2705 Tool '${toolToTrust}' trusted for this project only`);
10521
+ console.log(` Saved in: ${projectDir}/.bike4mind/local.json`);
10522
+ } catch (error) {
10523
+ console.error(`\u274C Failed to save project-local config:`, error);
10524
+ }
10525
+ break;
10526
+ }
10527
+ case "project": {
10528
+ if (!projectDir) {
10529
+ console.log('\u274C No project found. Use "global" to save to ~/.bike4mind/config.json');
10530
+ return;
10531
+ }
10532
+ try {
10533
+ const existingProject = await state.configStore.loadRawProjectConfig() || {};
10534
+ const updatedProject = {
10535
+ ...existingProject,
10536
+ tools: {
10537
+ ...existingProject.tools,
10538
+ enabled: [.../* @__PURE__ */ new Set([...existingProject.tools?.enabled || [], toolToTrust])]
10539
+ }
10540
+ };
10541
+ await state.configStore.saveProjectConfig(updatedProject);
10542
+ state.permissionManager?.trustTool(toolToTrust);
10543
+ console.log(`\u2705 Tool '${toolToTrust}' trusted for entire team`);
10544
+ console.log(` Saved in: ${projectDir}/.bike4mind/config.json`);
10545
+ console.log(" \u26A0\uFE0F Remember to commit this file to share with your team!");
10546
+ } catch (error) {
10547
+ console.error(`\u274C Failed to save project config:`, error);
10548
+ }
10549
+ break;
10550
+ }
10551
+ case "global": {
10552
+ try {
10553
+ await state.configStore.trustTool(toolToTrust);
10554
+ state.permissionManager?.trustTool(toolToTrust);
10555
+ console.log(`\u2705 Tool '${toolToTrust}' trusted globally (all projects)`);
10556
+ console.log(" Saved in: ~/.bike4mind/config.json");
10557
+ } catch (error) {
10558
+ console.error(`\u274C Failed to save global config:`, error);
10559
+ }
10560
+ break;
10561
+ }
10562
+ }
10563
+ };
10564
+ setState((prev) => ({
10565
+ ...prev,
10566
+ trustLocationSelector: {
10567
+ toolName: toolToTrust,
10568
+ resolve: handleLocationSelect
10569
+ }
10570
+ }));
10017
10571
  break;
10572
+ }
10018
10573
  case "untrust":
10019
10574
  if (!state.permissionManager) {
10020
10575
  console.log("Permission manager not initialized");
@@ -10190,6 +10745,49 @@ Usage by Model (Last ${USAGE_DAYS} days):`);
10190
10745
  console.log(`
10191
10746
  No usage data available for the last ${USAGE_DAYS} days.`);
10192
10747
  }
10748
+ break;
10749
+ }
10750
+ case "project-config": {
10751
+ const projectDir = state.configStore.getProjectConfigDir();
10752
+ const config = await state.configStore.get();
10753
+ console.log("\n\u{1F4C1} Project Configuration:\n");
10754
+ if (projectDir) {
10755
+ console.log(`Project Directory: ${projectDir}/.bike4mind/`);
10756
+ console.log("");
10757
+ const projectConfig = await state.configStore.loadRawProjectConfig();
10758
+ const localConfig = await state.configStore.loadRawProjectLocalConfig();
10759
+ if (projectConfig) {
10760
+ console.log("Team Config (.bike4mind/config.json):");
10761
+ if (projectConfig.defaultModel) {
10762
+ console.log(` Default Model: ${projectConfig.defaultModel}`);
10763
+ }
10764
+ if (projectConfig.tools?.denied && projectConfig.tools.denied.length > 0) {
10765
+ console.log(` Denied Tools: ${projectConfig.tools.denied.join(", ")}`);
10766
+ }
10767
+ if (projectConfig.mcpServers && projectConfig.mcpServers.length > 0) {
10768
+ console.log(` MCP Servers: ${projectConfig.mcpServers.map((s) => s.name).join(", ")}`);
10769
+ }
10770
+ console.log("");
10771
+ }
10772
+ if (localConfig) {
10773
+ console.log("Local Config (.bike4mind/local.json):");
10774
+ if (localConfig.trustedTools && localConfig.trustedTools.length > 0) {
10775
+ console.log(` Trusted Tools: ${localConfig.trustedTools.join(", ")}`);
10776
+ }
10777
+ if (localConfig.mcpServers && localConfig.mcpServers.length > 0) {
10778
+ console.log(` MCP Servers: ${localConfig.mcpServers.map((s) => s.name).join(", ")}`);
10779
+ }
10780
+ console.log("");
10781
+ }
10782
+ console.log("Merged Configuration (what you're using):");
10783
+ console.log(` Default Model: ${config.defaultModel}`);
10784
+ console.log(` Trusted Tools: ${config.trustedTools?.length || 0} tools`);
10785
+ console.log(` Denied Tools: ${config.tools.disabled.length} tools`);
10786
+ console.log(` MCP Servers: ${config.mcpServers.length} servers`);
10787
+ } else {
10788
+ console.log("No project configuration found.");
10789
+ console.log("Tip: Create .bike4mind/config.json in your project root for team-wide settings.");
10790
+ }
10193
10791
  console.log("");
10194
10792
  break;
10195
10793
  }
@@ -10203,11 +10801,31 @@ No usage data available for the last ${USAGE_DAYS} days.`);
10203
10801
  setState((prev) => ({ ...prev, config: updatedConfig }));
10204
10802
  };
10205
10803
  if (initError) {
10206
- return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React12.createElement(Text11, null, initError), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
10804
+ 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"));
10805
+ }
10806
+ if (state.trustLocationSelector) {
10807
+ const projectDir = state.configStore.getProjectConfigDir();
10808
+ const inProject = projectDir !== null;
10809
+ return /* @__PURE__ */ React13.createElement(
10810
+ TrustLocationSelector,
10811
+ {
10812
+ inProject,
10813
+ onSelect: (location) => {
10814
+ if (state.trustLocationSelector) {
10815
+ state.trustLocationSelector.resolve(location);
10816
+ }
10817
+ },
10818
+ onCancel: () => {
10819
+ if (state.trustLocationSelector) {
10820
+ state.trustLocationSelector.resolve(null);
10821
+ }
10822
+ }
10823
+ }
10824
+ );
10207
10825
  }
10208
10826
  if (state.showLoginFlow) {
10209
10827
  const loginApiUrl = getApiUrl(state.config?.apiConfig);
10210
- return /* @__PURE__ */ React12.createElement(
10828
+ return /* @__PURE__ */ React13.createElement(
10211
10829
  LoginFlow,
10212
10830
  {
10213
10831
  apiUrl: loginApiUrl,
@@ -10230,15 +10848,16 @@ No usage data available for the last ${USAGE_DAYS} days.`);
10230
10848
  );
10231
10849
  }
10232
10850
  if (!isInitialized) {
10233
- return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React12.createElement(Text11, null, "\u{1F680} Initializing..."));
10851
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "\u{1F680} Initializing..."));
10234
10852
  }
10235
- return /* @__PURE__ */ React12.createElement(
10853
+ return /* @__PURE__ */ React13.createElement(
10236
10854
  App,
10237
10855
  {
10238
10856
  onMessage: handleMessage,
10239
10857
  onCommand: handleCommand,
10240
10858
  commandHistory,
10241
10859
  config: state.config,
10860
+ availableModels: state.availableModels,
10242
10861
  onSaveConfig: handleSaveConfig,
10243
10862
  onPermissionResponse: (response) => {
10244
10863
  if (state.permissionPrompt) {
@@ -10265,6 +10884,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
10265
10884
  if (isDevMode) {
10266
10885
  console.log("\u{1F527} Running in development mode (using TypeScript source)\n");
10267
10886
  }
10268
- render(/* @__PURE__ */ React12.createElement(CliApp, null), {
10887
+ render(/* @__PURE__ */ React13.createElement(CliApp, null), {
10269
10888
  exitOnCtrlC: false
10270
10889
  });