@bike4mind/cli 0.22.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/{AgentHistoryStore-7zQUOxkA.mjs → AgentHistoryStore-kT9eNMRO.mjs} +8889 -7858
  2. package/dist/{ApiClient-XdY9O3nz.mjs → ApiClient-BOWpVvTq.mjs} +2 -2
  3. package/dist/{ConfigStore-8_0WsN5r.mjs → ConfigStore-DdHHCH2t.mjs} +1451 -249
  4. package/dist/{SandboxOrchestrator-BFPVpmB5.mjs → SandboxOrchestrator-BbMDgjzr.mjs} +1 -1
  5. package/dist/{SandboxOrchestrator-C8uleDn2.mjs → SandboxOrchestrator-CIegJrCj.mjs} +1 -1
  6. package/dist/{buildAgent-CvPRH2n-.mjs → buildAgent-B_kArQ_Y.mjs} +18 -9
  7. package/dist/commands/acpCommand.mjs +34 -13
  8. package/dist/commands/apiCommand.mjs +1 -1
  9. package/dist/commands/doctorCommand.mjs +1 -1
  10. package/dist/commands/envCommand.mjs +1 -1
  11. package/dist/commands/headlessCommand.mjs +73 -33
  12. package/dist/commands/mcpCommand.mjs +8 -13
  13. package/dist/commands/pluginCommand.mjs +9 -15
  14. package/dist/commands/updateCommand.mjs +1 -1
  15. package/dist/{createFile-DPv180yF-BnWFIxey.mjs → createFile-B8bur5Rb-CVzCarEA.mjs} +2 -2
  16. package/dist/{deleteFile-BdjUwUQF-B3XOJmg3.mjs → deleteFile-9B3gW_Nb-DG2sovIl.mjs} +2 -2
  17. package/dist/{globFiles-DjfDGaUK-CNR8pMRC.mjs → globFiles-CwJ8qmYo-BR5b2KvO.mjs} +3 -2
  18. package/dist/{grepSearch-BaYUfIYs-C-fxWc9G.mjs → grepSearch-BgoOOwGe-DtlV8Gn-.mjs} +4 -4
  19. package/dist/index.mjs +212 -47
  20. package/dist/{package-OZdXqn0e.mjs → package-Bqg2LSnH.mjs} +1 -1
  21. package/dist/{pathValidation-D8tjkQXE-1HwvsuYT.mjs → pathValidation-BRqf4HFX-CHwtwp3O.mjs} +7 -3
  22. package/dist/{serve-CEqSZwl6.mjs → serve-DO9Edl5S.mjs} +2 -2
  23. package/dist/types-CdIKgWWe.mjs +3 -0
  24. package/dist/{types-LyRNHOiS.mjs → types-F61_hxmG.mjs} +2 -0
  25. package/package.json +28 -28
  26. package/dist/types-CqscS34o.mjs +0 -3
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { $ as RemoteSkillSource, A as FallbackLlmBackend, C as createWriteTodosTool, D as createResumeAgentTool, E as createCoordinateTaskTool, G as DEFAULT_THOROUGHNESS, H as DEFAULT_AGENT_MODEL, I as PermissionManager, J as setWebSocketToolExecutor, K as clearFeatureModuleTools, N as findIterationBoundary, O as createBackgroundAgentTools, P as extractCompactInstructions, Q as isReadOnlyTool, R as getProcessHooks, S as createTodoStore, T as parseAgentConfig, U as DEFAULT_MAX_ITERATIONS, V as ALWAYS_DENIED_FOR_AGENTS, W as DEFAULT_RETRY_CONFIG, X as buildSystemPrompt, Y as getPlanModeFilePath, Z as buildSkillsPromptSection, _ as createDecisionStore, at as searchCommands, b as createWorkItemTools, c as isTransientNetworkError, ct as searchFiles, d as createReviewGateTool, et as CustomCommandStore, f as formatReviewGatesOutput, ft as MAX_PASTE_SIZE, g as createDecisionLogTool, h as formatBlockersOutput, it as processFileReferences, j as substituteArguments, k as createAgentDelegateTool, lt as warmFileCache, m as createBlockerTools, nt as SessionStore, ot as mergeCommands, p as createBlockerStore, q as registerFeatureModuleTools, rt as hasFileReferences, st as formatFileSize, tt as CheckpointStore, u as createReviewGateStore, ut as COMPACTION_SUMMARY_MARKER, v as formatDecisionsOutput, w as createSkillTool, x as createFindDefinitionTool, y as createGetFileStructureTool } from "./AgentHistoryStore-7zQUOxkA.mjs";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, B as ALWAYS_DENIED_FOR_AGENTS, C as createTodoStore, D as createCoordinateTaskTool, E as parseAgentConfig, F as PermissionManager, H as DEFAULT_MAX_ITERATIONS, J as setWebSocketToolExecutor, K as clearFeatureModuleTools, L as wrapTools, M as substituteArguments, O as createResumeAgentTool, P as findIterationBoundary, Q as RemoteSkillSource, R as getProcessHooks, S as createFindDefinitionTool, T as createSkillTool, U as DEFAULT_RETRY_CONFIG, V as DEFAULT_AGENT_MODEL, W as DEFAULT_THOROUGHNESS, X as buildSystemPrompt, Y as getPlanModeFilePath, Z as buildSkillsPromptSection, _ as createDecisionLogTool, at as mergeCommands, b as createGetFileStructureTool, ct as warmFileCache, d as createReviewGateStore, dt as MAX_PASTE_SIZE, et as CheckpointStore, f as createReviewGateTool, g as formatBlockersOutput, h as createBlockerTools, it as searchCommands, j as FallbackLlmBackend, k as createBackgroundAgentTools, l as isTransientNetworkError, lt as COMPACTION_SUMMARY_MARKER, m as createBlockerStore, nt as hasFileReferences, ot as formatFileSize, p as formatReviewGatesOutput, q as registerFeatureModuleTools, rt as processFileReferences, st as searchFiles, tt as SessionStore, v as createDecisionStore, w as createWriteTodosTool, x as createWorkItemTools, y as formatDecisionsOutput } from "./AgentHistoryStore-kT9eNMRO.mjs";
3
3
  import { a as selectLiveSubagentTokens, i as selectLiveSubagentCredits, n as selectActiveBackgroundShells, o as useCliStore, r as selectCompletedBackgroundShells, t as selectActiveBackgroundAgents } from "./store-CvjTpQPs.mjs";
4
4
  import { r as isFeatureEnabled, t as PluginStore } from "./PluginStore-DwvOJ-G3.mjs";
5
- import { $t as selfClaimedActorKindSchema, Qt as actorKindSchema, X as REVIEW_GATE_STATUS_VALUES, Xt as actorColorIndex, Yt as ACTOR_COLOR_SLOTS, Z as SUBQUEST_STATUS_VALUES, Zt as actorKindMarker, a as getCreditsUrl, b as CREDIT_DEDUCT_TRANSACTION_TYPES, bt as isEarlyStop, c as requireApiUrl, i as LOCAL_DEV_URL, l as resolveApiEndpoint, n as logger, o as getEnvironmentName, r as ApiEndpointUnconfiguredError, s as parseApiUrl, t as ConfigStore, x as ChatModels } from "./ConfigStore-8_0WsN5r.mjs";
5
+ import { D as ChatModels, E as CREDIT_DEDUCT_TRANSACTION_TYPES, Ot as isEarlyStop, c as ApiEndpointUnconfiguredError, cn as actorColorIndex, d as getEnvironmentName, dn as selfClaimedActorKindSchema, et as REVIEW_GATE_STATUS_VALUES, f as parseApiUrl, l as LOCAL_DEV_URL, ln as actorKindMarker, m as resolveApiEndpoint, n as logger, p as requireApiUrl, r as extractCompactInstructions, s as isReadOnlyTool, sn as ACTOR_COLOR_SLOTS, t as ConfigStore, tt as SUBQUEST_STATUS_VALUES, u as getCreditsUrl, un as actorKindSchema } from "./ConfigStore-DdHHCH2t.mjs";
6
6
  import { t as getShellSessionManager } from "./ShellSessionManager-6o8KZzl1-vrbPAUTq.mjs";
7
- import { n as OAuthClient, t as ApiClient } from "./ApiClient-XdY9O3nz.mjs";
7
+ import { n as OAuthClient, t as ApiClient } from "./ApiClient-BOWpVvTq.mjs";
8
8
  import { i as getTokenCounter, n as ConversationContext, r as reconstructTurnBlocks, t as WorkItemsClient } from "./WorkItemsClient-Cow6nXx7.mjs";
9
- import { a as createToolSearchTool, i as buildLlmBackend, n as buildSupportingStores, o as deferredToolRegistry, r as buildSandbox, s as NotifyingLlmBackend, t as buildAgent } from "./buildAgent-CvPRH2n-.mjs";
10
- import { t as version } from "./package-OZdXqn0e.mjs";
9
+ import { a as createToolSearchTool, i as buildLlmBackend, n as buildSupportingStores, o as deferredToolRegistry, r as buildSandbox, s as NotifyingLlmBackend, t as buildAgent } from "./buildAgent-B_kArQ_Y.mjs";
10
+ import { t as version } from "./package-Bqg2LSnH.mjs";
11
11
  import { r as checkForUpdate } from "./updateChecker-CQW8bxo6.mjs";
12
12
  import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
13
13
  import { Box, Static, Text, render, useApp, useInput, usePaste, useStdout } from "ink";
@@ -1083,9 +1083,19 @@ function CompletedGroupNotification() {
1083
1083
  }
1084
1084
  //#endregion
1085
1085
  //#region src/components/PermissionPrompt.tsx
1086
+ /**
1087
+ * Escape terminal control characters (carriage return, ESC/ANSI, other C0
1088
+ * controls, DEL) into a visible `\xHH` form. The preview is model-authored;
1089
+ * rendering it raw would let a `\r` or ESC sequence rewrite what the user sees,
1090
+ * so the approved text could differ from what actually runs. Tab and newline are
1091
+ * left intact (newline is already the line delimiter).
1092
+ */
1093
+ function escapeTerminalControlChars(text) {
1094
+ return text.replace(/[\x00-\x08\x0b-\x1f\x7f]/g, (ch) => `\\x${ch.charCodeAt(0).toString(16).padStart(2, "0")}`);
1095
+ }
1086
1096
  /** Render a diff/preview string, color-coding each line by its prefix (see per-branch comments below). */
1087
1097
  function renderDiffPreview(preview) {
1088
- return preview.split("\n").map((line, index) => {
1098
+ return escapeTerminalControlChars(preview).split("\n").map((line, index) => {
1089
1099
  if (line.startsWith("🛑")) return /* @__PURE__ */ React.createElement(Text, {
1090
1100
  key: index,
1091
1101
  bold: true,
@@ -2656,6 +2666,40 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
2656
2666
  }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Use ↑↓ arrows to navigate, Enter to select, Ctrl+C to cancel")));
2657
2667
  }
2658
2668
  //#endregion
2669
+ //#region src/components/FolderTrustPrompt.tsx
2670
+ /**
2671
+ * One-time startup prompt shown when the current project root ships repo-committed
2672
+ * b4m files (config, .mcp.json, agents, skills, commands) but has not been
2673
+ * trusted. Until trusted, those files are inert - no config merge, no MCP spawn,
2674
+ * no agent/skill/command load. "Trust this folder" persists the decision; "Not
2675
+ * now" keeps the project untrusted for this session (re-prompts next launch).
2676
+ *
2677
+ * "Not now" is listed first so it is the preselected default: Enter-through on a
2678
+ * freshly cloned repo must not grant trust.
2679
+ */
2680
+ function FolderTrustPrompt({ projectRoot, onSelect }) {
2681
+ return /* @__PURE__ */ React.createElement(Box, {
2682
+ flexDirection: "column",
2683
+ marginY: 1
2684
+ }, /* @__PURE__ */ React.createElement(Box, {
2685
+ marginBottom: 1,
2686
+ flexDirection: "column"
2687
+ }, /* @__PURE__ */ React.createElement(Text, {
2688
+ bold: true,
2689
+ color: "yellow"
2690
+ }, "This project ships Bike4Mind config that can run code."), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, projectRoot), /* @__PURE__ */ React.createElement(Text, null, "Repo-committed config, MCP servers, agents and skills stay inert until you trust this folder. Only trust folders you would run code from.")), /* @__PURE__ */ React.createElement(SelectInput, {
2691
+ items: [{
2692
+ label: "Not now - keep repo config inert this session",
2693
+ value: "not-now"
2694
+ }, {
2695
+ label: "Trust this folder - load its config, agents, skills and MCP servers",
2696
+ value: "trust"
2697
+ }],
2698
+ onSelect: (item) => onSelect(item.value),
2699
+ itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: isSelected ? "cyan" : void 0 }, isSelected ? "❯ " : " ", label))
2700
+ }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Use ↑↓ arrows to navigate, Enter to select. You can change this later with /trust folder.")));
2701
+ }
2702
+ //#endregion
2659
2703
  //#region src/components/RewindSelector.tsx
2660
2704
  function RewindSelector({ messages, onSelect, onCancel }) {
2661
2705
  const [step, setStep] = useState("selection");
@@ -2882,6 +2926,22 @@ function EnvironmentPicker({ onSelect }) {
2882
2926
  }
2883
2927
  //#endregion
2884
2928
  //#region src/components/LoginFlow.tsx
2929
+ /**
2930
+ * Only https URLs (and http on localhost for dev) may be handed to the OS opener.
2931
+ * The device-flow response is server-controlled, so a hostile/compromised server
2932
+ * could return `file:///...`, `javascript:...` or similar and have the OS launch
2933
+ * it. Reject anything that isn't a plain web URL.
2934
+ */
2935
+ function isBrowserOpenableUrl(raw) {
2936
+ let url;
2937
+ try {
2938
+ url = new URL(raw);
2939
+ } catch {
2940
+ return false;
2941
+ }
2942
+ if (url.protocol === "https:") return true;
2943
+ return url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1");
2944
+ }
2885
2945
  function extractErrorMessage(err) {
2886
2946
  if (axios.isAxiosError(err)) {
2887
2947
  if (err.response?.data) {
@@ -2939,9 +2999,10 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
2939
2999
  onError
2940
3000
  ]);
2941
3001
  useEffect(() => {
2942
- if (deviceFlow && status === "waiting") open(deviceFlow.verification_uri_complete).catch((err) => {
3002
+ if (deviceFlow && status === "waiting") if (isBrowserOpenableUrl(deviceFlow.verification_uri_complete)) open(deviceFlow.verification_uri_complete).catch((err) => {
2943
3003
  console.error("Failed to auto-open browser:", err);
2944
3004
  });
3005
+ else console.error("Refusing to auto-open untrusted verification URL:", deviceFlow.verification_uri_complete);
2945
3006
  }, [deviceFlow, status]);
2946
3007
  if (status === "initiating") return /* @__PURE__ */ React.createElement(Box, {
2947
3008
  flexDirection: "column",
@@ -3087,6 +3148,35 @@ var CommandHistoryStore = class {
3087
3148
  }
3088
3149
  };
3089
3150
  //#endregion
3151
+ //#region src/storage/buildGlobalConfigPatch.ts
3152
+ /**
3153
+ * Compute the minimal global-config patch for a /config editor save: only the
3154
+ * editor-owned fields (defaultModel, preferences, features) that the user
3155
+ * actually changed vs the current effective (merged) config. Unchanged fields
3156
+ * are omitted, so a repo-injected value the user never touched is never
3157
+ * laundered into the global layer by a save() that spreads the merged config.
3158
+ *
3159
+ * defaultModel and preferences diff per key. features stays whole-object when
3160
+ * anything changed, on purpose: ConfigStore.save() feeds config.features to
3161
+ * mergeFeatures as the caller's COMPLETE desired set (absence of a key means
3162
+ * "removed"), so a per-key diff would make save() delete untouched keys. That
3163
+ * whole-object features path is only latently launderable today (no repo layer
3164
+ * populates features); tightening it needs a coordinated change to save()'s
3165
+ * concurrent-writer merge and is tracked separately.
3166
+ */
3167
+ function buildGlobalConfigPatch(prev, next) {
3168
+ const patch = {};
3169
+ if (next.defaultModel !== prev?.defaultModel) patch.defaultModel = next.defaultModel;
3170
+ const changedPreferences = {};
3171
+ for (const [key, value] of Object.entries(next.preferences)) {
3172
+ const prevValue = (prev?.preferences)?.[key];
3173
+ if (JSON.stringify(value) !== JSON.stringify(prevValue)) changedPreferences[key] = value;
3174
+ }
3175
+ if (Object.keys(changedPreferences).length > 0) patch.preferences = changedPreferences;
3176
+ if (JSON.stringify(next.features ?? {}) !== JSON.stringify(prev?.features ?? {})) patch.features = next.features;
3177
+ return patch;
3178
+ }
3179
+ //#endregion
3090
3180
  //#region src/utils/formatStep.ts
3091
3181
  const MAX_INPUT_LENGTH = 500;
3092
3182
  /**
@@ -7069,7 +7159,9 @@ API Configuration:
7069
7159
  Tool Permissions:
7070
7160
  /trust <tool-name> - Trust a tool (won't ask permission again)
7071
7161
  /untrust <tool-name> - Remove tool from trusted list
7072
- /trusted - List all trusted tools
7162
+ /trust folder - Trust this project (load its repo config, agents, skills, MCP)
7163
+ /untrust folder - Revoke trust for this project (repo config goes inert)
7164
+ /trusted - Show folder-trust status and list all trusted tools
7073
7165
 
7074
7166
  Project Configuration:
7075
7167
  /project-config - Show merged project configuration
@@ -7131,8 +7223,16 @@ Multi-line Input:
7131
7223
  {
7132
7224
  name: "trusted",
7133
7225
  run: (_args, ctx) => {
7226
+ const projectRoot = ctx.configStore.getProjectRealPath();
7227
+ console.log("\n📁 Folder Trust:\n");
7228
+ if (!projectRoot) console.log(" No project root discovered.");
7229
+ else {
7230
+ const isTrusted = ctx.configStore.isProjectTrusted();
7231
+ console.log(` ${isTrusted ? "✅ Trusted" : "⛔ Not trusted"}: ${projectRoot}`);
7232
+ if (!isTrusted) console.log(" Repo config/agents/skills/MCP stay inert. Trust with /trust folder.");
7233
+ }
7134
7234
  if (!ctx.permissionManager) {
7135
- console.log("Permission manager not initialized");
7235
+ console.log("\nPermission manager not initialized\n");
7136
7236
  return;
7137
7237
  }
7138
7238
  const trustedTools = ctx.permissionManager.getTrustedTools();
@@ -7753,6 +7853,7 @@ function CliApp() {
7753
7853
  permissionManager: null,
7754
7854
  permissionPrompt: null,
7755
7855
  trustLocationSelector: null,
7856
+ folderTrustPrompt: null,
7756
7857
  rewindSelector: null,
7757
7858
  sessionSelector: null,
7758
7859
  orchestrator: null,
@@ -7771,6 +7872,7 @@ function CliApp() {
7771
7872
  const [initError, setInitError] = useState(null);
7772
7873
  const [commandHistory, setCommandHistory] = useState([]);
7773
7874
  const imageStoreInitPromise = useRef(null);
7875
+ const folderTrustResolvedRef = useRef(false);
7774
7876
  const todoStoreRef = useRef(createTodoStore());
7775
7877
  const decisionStoreRef = useRef(createDecisionStore());
7776
7878
  const blockerStoreRef = useRef(createBlockerStore());
@@ -7877,11 +7979,21 @@ function CliApp() {
7877
7979
  try {
7878
7980
  const startupLog = [];
7879
7981
  const config = await state.configStore.load();
7982
+ if (!folderTrustResolvedRef.current && !state.configStore.isProjectTrusted() && state.configStore.projectHasB4mFiles() && Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY)) {
7983
+ folderTrustResolvedRef.current = true;
7984
+ setState((prev) => ({
7985
+ ...prev,
7986
+ folderTrustPrompt: { projectRoot: state.configStore.getProjectRealPath() ?? process.cwd() },
7987
+ config
7988
+ }));
7989
+ return;
7990
+ }
7880
7991
  const configDirs = await state.configStore.getAdditionalDirectories();
7881
7992
  const flagDirs = process.env.B4M_ADDITIONAL_DIRS ? JSON.parse(process.env.B4M_ADDITIONAL_DIRS) : [];
7882
7993
  const additionalDirectories = [.../* @__PURE__ */ new Set([...configDirs, ...flagDirs])];
7883
7994
  const history = await state.commandHistoryStore.load();
7884
7995
  setCommandHistory(history);
7996
+ state.customCommandStore.setProjectTrusted(state.configStore.isProjectTrusted());
7885
7997
  try {
7886
7998
  await state.customCommandStore.loadCommands();
7887
7999
  } catch (error) {
@@ -8108,14 +8220,26 @@ function CliApp() {
8108
8220
  blockerStoreRef.current.blockers = [];
8109
8221
  reviewGateStoreRef.current.reviewGates = [];
8110
8222
  }
8223
+ const cliWrapDeps = {
8224
+ permissionManager,
8225
+ showPermissionPrompt: promptFn,
8226
+ agentContext,
8227
+ configStore: state.configStore,
8228
+ apiClient,
8229
+ sandboxOrchestrator: sandboxOrchestrator ?? void 0,
8230
+ allowedDirectories: additionalDirectories
8231
+ };
8111
8232
  const enableSkillTool = config.preferences.enableSkillTool !== false;
8112
- const skillTool = enableSkillTool ? createSkillTool({
8233
+ const skillTool = enableSkillTool ? wrapTools([createSkillTool({
8113
8234
  customCommandStore: state.customCommandStore,
8114
8235
  subagentOrchestrator: orchestrator,
8115
- sessionId: newSession.id
8116
- }) : null;
8117
- const findDefinitionTool = createFindDefinitionTool();
8118
- const getFileStructureTool = createGetFileStructureTool();
8236
+ sessionId: newSession.id,
8237
+ permissionManager,
8238
+ promptFn,
8239
+ allowedDirectories: additionalDirectories
8240
+ })], cliWrapDeps)[0] : null;
8241
+ const findDefinitionTool = wrapTools([createFindDefinitionTool(additionalDirectories)], cliWrapDeps)[0];
8242
+ const getFileStructureTool = wrapTools([createGetFileStructureTool(additionalDirectories)], cliWrapDeps)[0];
8119
8243
  const workItemTools = config.preferences.enableWorkItemTools ? createWorkItemTools(new WorkItemsClient(apiClient)) : [];
8120
8244
  const cliTools = [
8121
8245
  agentDelegateTool,
@@ -9142,6 +9266,18 @@ function CliApp() {
9142
9266
  break;
9143
9267
  }
9144
9268
  case "trust": {
9269
+ if (args[0] === "folder") {
9270
+ const root = state.configStore.getProjectRealPath() ?? process.cwd();
9271
+ if (!await state.configStore.trustProject()) {
9272
+ console.log("\n⚠️ No project folder to trust here (no resolvable project root).\n");
9273
+ return;
9274
+ }
9275
+ state.customCommandStore.setProjectTrusted(true);
9276
+ await state.customCommandStore.reloadCommands().catch(() => {});
9277
+ console.log(`\n✅ Trusted project folder: ${root}`);
9278
+ console.log("Project skills are now loaded. Restart b4m to load repo agents and MCP servers.\n");
9279
+ return;
9280
+ }
9145
9281
  if (!state.permissionManager) {
9146
9282
  console.log("Permission manager not initialized");
9147
9283
  return;
@@ -9177,6 +9313,11 @@ function CliApp() {
9177
9313
  console.log("❌ No project found. Use \"global\" to save to ~/.bike4mind/config.json");
9178
9314
  return;
9179
9315
  }
9316
+ if (!state.configStore.isProjectTrusted()) {
9317
+ console.log("❌ This folder is not trusted, so a project-local trust would not take effect.");
9318
+ console.log(` Run /trust folder first, or choose "global" to trust '${toolToTrust}' everywhere.`);
9319
+ return;
9320
+ }
9180
9321
  try {
9181
9322
  await state.configStore.initProjectConfig();
9182
9323
  const existingLocal = await state.configStore.loadRawProjectLocalConfig() || {};
@@ -9197,6 +9338,11 @@ function CliApp() {
9197
9338
  console.log("❌ No project found. Use \"global\" to save to ~/.bike4mind/config.json");
9198
9339
  return;
9199
9340
  }
9341
+ if (!state.configStore.isProjectTrusted()) {
9342
+ console.log("❌ This folder is not trusted, so a team-project trust would not take effect.");
9343
+ console.log(` Run /trust folder first, or choose "global" to trust '${toolToTrust}' everywhere.`);
9344
+ return;
9345
+ }
9200
9346
  try {
9201
9347
  const existingProject = await state.configStore.loadRawProjectConfig() || {};
9202
9348
  const updatedProject = {
@@ -9235,6 +9381,15 @@ function CliApp() {
9235
9381
  break;
9236
9382
  }
9237
9383
  case "untrust": {
9384
+ if (args[0] === "folder") {
9385
+ const root = state.configStore.getProjectRealPath() ?? process.cwd();
9386
+ await state.configStore.untrustProject();
9387
+ state.customCommandStore.setProjectTrusted(false);
9388
+ await state.customCommandStore.reloadCommands().catch(() => {});
9389
+ console.log(`\n✅ Untrusted project folder: ${root}`);
9390
+ console.log("Repo config stays inert. Restart b4m to fully unload repo agents and MCP servers.\n");
9391
+ return;
9392
+ }
9238
9393
  if (!state.permissionManager) {
9239
9394
  console.log("Permission manager not initialized");
9240
9395
  return;
@@ -9568,6 +9723,7 @@ function CliApp() {
9568
9723
  console.log("\n📁 Project Configuration:\n");
9569
9724
  if (projectDir) {
9570
9725
  console.log(`Project Directory: ${projectDir}/.bike4mind/`);
9726
+ if (!state.configStore.isProjectTrusted()) console.log("⚠️ Folder not trusted - team/local config below is NOT applied. Run /trust folder.");
9571
9727
  console.log("");
9572
9728
  const projectConfig = await state.configStore.loadRawProjectConfig();
9573
9729
  const localConfig = await state.configStore.loadRawProjectLocalConfig();
@@ -9769,7 +9925,7 @@ function CliApp() {
9769
9925
  }
9770
9926
  break;
9771
9927
  }
9772
- case "sandbox:enable": {
9928
+ case "sandbox:enable":
9773
9929
  if (!state.sandboxOrchestrator) {
9774
9930
  console.log("Sandbox not initialized");
9775
9931
  break;
@@ -9785,15 +9941,10 @@ function CliApp() {
9785
9941
  const pm = state.sandboxOrchestrator.getProxyManager();
9786
9942
  if (pm?.isRunning()) console.log(`🌐 Network proxy started on port ${pm.getPort()}`);
9787
9943
  }
9788
- const config = await state.configStore.get();
9789
- await state.configStore.save({
9790
- ...config,
9791
- sandbox: { ...state.sandboxOrchestrator.getConfig() }
9792
- });
9944
+ await state.configStore.saveSandboxConfig(state.sandboxOrchestrator.getConfig());
9793
9945
  console.log("Sandbox enabled (auto-allow mode)");
9794
9946
  break;
9795
- }
9796
- case "sandbox:disable": {
9947
+ case "sandbox:disable":
9797
9948
  if (!state.sandboxOrchestrator) {
9798
9949
  console.log("Sandbox not initialized");
9799
9950
  break;
@@ -9801,14 +9952,9 @@ function CliApp() {
9801
9952
  await state.sandboxOrchestrator.stopProxy();
9802
9953
  state.sandboxOrchestrator.setMode("disabled");
9803
9954
  state.permissionManager?.setSandboxState("disabled", false);
9804
- const disableConfig = await state.configStore.get();
9805
- await state.configStore.save({
9806
- ...disableConfig,
9807
- sandbox: { ...state.sandboxOrchestrator.getConfig() }
9808
- });
9955
+ await state.configStore.saveSandboxConfig(state.sandboxOrchestrator.getConfig());
9809
9956
  console.log("Sandbox disabled");
9810
9957
  break;
9811
- }
9812
9958
  case "sandbox:mode": {
9813
9959
  if (!state.sandboxOrchestrator) {
9814
9960
  console.log("Sandbox not initialized");
@@ -9825,11 +9971,7 @@ function CliApp() {
9825
9971
  }
9826
9972
  state.sandboxOrchestrator.setMode(modeArg);
9827
9973
  state.permissionManager?.setSandboxState(modeArg, state.sandboxOrchestrator.isActive());
9828
- const modeConfig = await state.configStore.get();
9829
- await state.configStore.save({
9830
- ...modeConfig,
9831
- sandbox: { ...state.sandboxOrchestrator.getConfig() }
9832
- });
9974
+ await state.configStore.saveSandboxConfig(state.sandboxOrchestrator.getConfig());
9833
9975
  console.log(`Sandbox mode set to: ${modeArg}`);
9834
9976
  break;
9835
9977
  }
@@ -9851,16 +9993,12 @@ function CliApp() {
9851
9993
  proxyMgr.addAllowedDomain(domain);
9852
9994
  console.log(` Added: ${domain}`);
9853
9995
  }
9854
- const trustDomainConfig = await state.configStore.get();
9855
9996
  const currentSandboxConfig = state.sandboxOrchestrator.getConfig();
9856
- await state.configStore.save({
9857
- ...trustDomainConfig,
9858
- sandbox: {
9859
- ...currentSandboxConfig,
9860
- network: {
9861
- ...currentSandboxConfig.network,
9862
- allowedDomains: proxyMgr.getAllowedDomains()
9863
- }
9997
+ await state.configStore.saveSandboxConfig({
9998
+ ...currentSandboxConfig,
9999
+ network: {
10000
+ ...currentSandboxConfig.network,
10001
+ allowedDomains: proxyMgr.getAllowedDomains()
9864
10002
  }
9865
10003
  });
9866
10004
  console.log(`Trusted ${args.length} domain(s)`);
@@ -10039,7 +10177,8 @@ function CliApp() {
10039
10177
  * Handle saving config from the interactive editor
10040
10178
  */
10041
10179
  const handleSaveConfig = async (updatedConfig, options) => {
10042
- await state.configStore.save(updatedConfig);
10180
+ const patch = buildGlobalConfigPatch(state.config, updatedConfig);
10181
+ await state.configStore.save(patch);
10043
10182
  const modelChanged = state.config?.defaultModel !== updatedConfig.defaultModel;
10044
10183
  const featuresChanged = JSON.stringify(state.config?.features ?? {}) !== JSON.stringify(updatedConfig.features ?? {});
10045
10184
  let newFeatureRegistry = state.featureRegistry;
@@ -10127,10 +10266,14 @@ function CliApp() {
10127
10266
  isBusy: () => useCliStore.getState().isThinking,
10128
10267
  saveModel: async (modelId) => {
10129
10268
  if (!state.config) throw new Error("no CLI config is loaded");
10130
- await handleSaveConfig({
10131
- ...state.config,
10132
- defaultModel: modelId
10133
- }, { skipModelApply: true });
10269
+ await state.configStore.save({ defaultModel: modelId });
10270
+ setState((prev) => prev.config ? {
10271
+ ...prev,
10272
+ config: {
10273
+ ...prev.config,
10274
+ defaultModel: modelId
10275
+ }
10276
+ } : prev);
10134
10277
  },
10135
10278
  applyToSession: applyModelToSession,
10136
10279
  log: (message) => console.log(message),
@@ -10155,6 +10298,28 @@ function CliApp() {
10155
10298
  }
10156
10299
  });
10157
10300
  }
10301
+ if (state.folderTrustPrompt) return /* @__PURE__ */ React.createElement(FolderTrustPrompt, {
10302
+ projectRoot: state.folderTrustPrompt.projectRoot,
10303
+ onSelect: (choice) => {
10304
+ (async () => {
10305
+ try {
10306
+ if (choice === "trust") {
10307
+ await state.configStore.trustProject();
10308
+ console.log("\n✅ Folder trusted. Loading its config, agents, skills and MCP servers.\n");
10309
+ } else console.log("\nℹ️ Project config stays inert this session. Use /trust folder to trust it later.\n");
10310
+ } finally {
10311
+ setState((prev) => ({
10312
+ ...prev,
10313
+ folderTrustPrompt: null
10314
+ }));
10315
+ init().catch((err) => {
10316
+ console.error("\n❌ Initialization failed:", err instanceof Error ? err.message : String(err), "\n");
10317
+ exit();
10318
+ });
10319
+ }
10320
+ })();
10321
+ }
10322
+ });
10158
10323
  const currentSession = useCliStore.getState().session;
10159
10324
  if (state.rewindSelector && currentSession) return /* @__PURE__ */ React.createElement(RewindSelector, {
10160
10325
  messages: currentSession.messages,
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  //#region package.json
3
- var version = "0.22.0";
3
+ var version = "1.1.0";
4
4
  //#endregion
5
5
  export { version as t };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { realpathSync } from "fs";
2
+ import { readlinkSync, realpathSync } from "fs";
3
3
  import path from "path";
4
- //#region ../../b4m-core/services/dist/pathValidation-D8tjkQXE.mjs
4
+ //#region ../../b4m-core/services/dist/pathValidation-BRqf4HFX.mjs
5
5
  /**
6
6
  * Resolves a path to its canonical form, following symlinks.
7
7
  * If the path doesn't exist (e.g., file being created), resolves the
@@ -10,7 +10,11 @@ import path from "path";
10
10
  function resolveRealPath(filePath) {
11
11
  try {
12
12
  return realpathSync(filePath);
13
- } catch {
13
+ } catch (err) {
14
+ if (err?.code === "ENOENT") try {
15
+ const linkTarget = readlinkSync(filePath);
16
+ return resolveRealPath(path.isAbsolute(linkTarget) ? linkTarget : path.resolve(path.dirname(filePath), linkTarget));
17
+ } catch {}
14
18
  const parentDir = path.dirname(filePath);
15
19
  const basename = path.basename(filePath);
16
20
  if (parentDir === filePath) return filePath;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { W as PROMPT_TEXT_MAX, c as requireApiUrl, d as AGENT_QUEST_MANIFEST, f as AGENT_QUEST_MCP_URI, n as logger, s as parseApiUrl, t as ConfigStore, u as AGENT_QUEST_ID } from "./ConfigStore-8_0WsN5r.mjs";
3
- import { t as ApiClient } from "./ApiClient-XdY9O3nz.mjs";
2
+ import { J as PROMPT_TEXT_MAX, _ as AGENT_QUEST_MCP_URI, f as parseApiUrl, g as AGENT_QUEST_MANIFEST, h as AGENT_QUEST_ID, n as logger, p as requireApiUrl, t as ConfigStore } from "./ConfigStore-DdHHCH2t.mjs";
3
+ import { t as ApiClient } from "./ApiClient-BOWpVvTq.mjs";
4
4
  import { z as z$1 } from "zod";
5
5
  import { isAxiosError } from "axios";
6
6
  import { Writable } from "node:stream";
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { t as DEFAULT_SANDBOX_CONFIG } from "./types-F61_hxmG.mjs";
3
+ export { DEFAULT_SANDBOX_CONFIG };
@@ -16,6 +16,8 @@ const DEFAULT_SANDBOX_CONFIG = {
16
16
  "$HOME/.aws",
17
17
  "$HOME/.gnupg",
18
18
  "$HOME/.env",
19
+ "$HOME/.claude",
20
+ "$HOME/.bike4mind",
19
21
  "/etc/shadow",
20
22
  "/etc/passwd"
21
23
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.22.0",
3
+ "version": "1.1.0",
4
4
  "mcpName": "io.github.bike4mind/bike4mind",
5
5
  "type": "module",
6
6
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
@@ -56,57 +56,57 @@
56
56
  "@smithy/node-http-handler": "^4.9.3",
57
57
  "async-mutex": "^0.5.0",
58
58
  "axios": "1.18.0",
59
- "bcryptjs": "^3.0.2",
60
- "better-sqlite3": "^13.0.1",
59
+ "bcryptjs": "^3.0.3",
60
+ "better-sqlite3": "^13.0.3",
61
61
  "cheerio": "1.2.0",
62
- "chess.js": "^1.0.0-beta.8",
62
+ "chess.js": "^1.4.0",
63
63
  "cli-highlight": "^2.1.11",
64
- "csv-parse": "^7.0.1",
65
- "dayjs": "^1.11.21",
64
+ "csv-parse": "^7.0.2",
65
+ "dayjs": "^1.11.23",
66
66
  "diff": "^9.0.0",
67
67
  "dotenv": "^17.4.2",
68
68
  "eventsource-parser": "^3.1.0",
69
69
  "fdir": "^6.5.0",
70
- "file-type": "^22.0.1",
71
- "fuse.js": "^7.4.2",
70
+ "file-type": "^22.1.0",
71
+ "fuse.js": "^7.5.0",
72
72
  "fzf": "^0.5.2",
73
73
  "glob": "^13.0.6",
74
74
  "gray-matter": "^4.0.3",
75
- "ignore": "^7.0.6",
76
- "ink": "^7.1.0",
75
+ "ignore": "^7.0.9",
76
+ "ink": "^7.1.1",
77
77
  "ink-select-input": "^6.2.0",
78
78
  "ink-spinner": "^5.0.0",
79
79
  "ink-text-input": "^6.0.0",
80
80
  "jsonwebtoken": "^9.0.3",
81
- "jszip": "^3.10.1",
81
+ "jszip": "^3.10.2",
82
82
  "lodash": "^4.18.1",
83
- "mammoth": "^1.12.0",
83
+ "mammoth": "^1.12.3",
84
84
  "marked": "^15.0.11",
85
85
  "mathjs": "^15.2.0",
86
86
  "mime-types": "^3.0.2",
87
87
  "mongoose": "^8.24.1",
88
88
  "ollama": "^0.6.3",
89
- "open": "^11.0.0",
89
+ "open": "^11.0.4",
90
90
  "openai": "^6.46.0",
91
- "p-limit": "^7.3.0",
92
- "picomatch": "^4.0.5",
91
+ "p-limit": "^7.3.2",
92
+ "picomatch": "^4.0.7",
93
93
  "qrcode": "^1.5.4",
94
94
  "react": "^19.2.7",
95
- "sharp": "^0.35.3",
95
+ "sharp": "^0.35.4",
96
96
  "shell-quote": "^1.10.0",
97
97
  "speakeasy": "^2.0.0",
98
98
  "tiktoken": "^1.0.22",
99
- "tldts": "^7.4.8",
99
+ "tldts": "^7.4.13",
100
100
  "tree-sitter-wasms": "^0.1.13",
101
101
  "turndown": "^7.2.4",
102
102
  "undici": "^7.29.0",
103
103
  "unpdf": "^1.6.2",
104
- "uuid": "^14.0.1",
104
+ "uuid": "^14.0.2",
105
105
  "voyageai": "^0.4.0",
106
106
  "web-tree-sitter": "0.25.10",
107
- "ws": "^8.21.0",
107
+ "ws": "^8.21.3",
108
108
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
109
- "yargs": "^18.0.0",
109
+ "yargs": "^18.1.0",
110
110
  "yauzl": "^3.4.0",
111
111
  "zod": "^4.4.3",
112
112
  "zod-validation-error": "^5.0.0",
@@ -126,15 +126,15 @@
126
126
  "tsx": "^4.23.0",
127
127
  "typescript": "^5.9.3",
128
128
  "vitest": "^4.1.10",
129
- "@bike4mind/agents": "1.0.3",
130
- "@bike4mind/fab-pipeline": "1.3.3",
131
- "@bike4mind/llm-adapters": "0.14.0",
129
+ "@bike4mind/agents": "0.0.0-snapshot-changeset-release-main-20260922072433",
130
+ "@bike4mind/common": "0.0.0-snapshot-changeset-release-main-20260922072433",
131
+ "@bike4mind/fab-pipeline": "0.0.0-snapshot-changeset-release-main-20260922072433",
132
132
  "@bike4mind/hearth": "0.3.0",
133
- "@bike4mind/common": "7.2.0",
134
- "@bike4mind/mcp": "2.0.3",
135
- "@bike4mind/observability": "0.2.0",
136
- "@bike4mind/services": "7.2.0",
137
- "@bike4mind/utils": "5.1.0"
133
+ "@bike4mind/llm-adapters": "0.0.0-snapshot-changeset-release-main-20260922072433",
134
+ "@bike4mind/mcp": "0.0.0-snapshot-changeset-release-main-20260922072433",
135
+ "@bike4mind/observability": "0.2.1",
136
+ "@bike4mind/services": "0.0.0-snapshot-changeset-release-main-20260922072433",
137
+ "@bike4mind/utils": "0.0.0-snapshot-changeset-release-main-20260922072433"
138
138
  },
139
139
  "optionalDependencies": {
140
140
  "@vscode/ripgrep": "^1.18.0"
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as DEFAULT_SANDBOX_CONFIG } from "./types-LyRNHOiS.mjs";
3
- export { DEFAULT_SANDBOX_CONFIG };