@bike4mind/cli 0.2.34 → 0.2.35-fix-reply-disappears-on-refresh.20009

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.
@@ -46,6 +46,11 @@ const argv = await yargs(hideBin(process.argv))
46
46
  description: 'Disable loading project-specific configuration (.bike4mind/)',
47
47
  default: false,
48
48
  })
49
+ .option('add-dir', {
50
+ type: 'array',
51
+ description: 'Add additional directories for file access (can be used multiple times)',
52
+ string: true,
53
+ })
49
54
  .command('mcp', 'Manage MCP (Model Context Protocol) servers', (yargs) => {
50
55
  return yargs
51
56
  .command('list', 'List configured MCP servers', {}, async () => {
@@ -109,6 +114,12 @@ if (argv['debug-stream']) {
109
114
  if (argv['no-project-config']) {
110
115
  process.env.B4M_NO_PROJECT_CONFIG = '1';
111
116
  }
117
+ if (argv['add-dir'] && argv['add-dir'].length > 0) {
118
+ // Resolve paths to absolute and pass via environment variable
119
+ const { resolve } = await import('path');
120
+ const resolvedDirs = argv['add-dir'].map(d => resolve(d));
121
+ process.env.B4M_ADDITIONAL_DIRS = JSON.stringify(resolvedDirs);
122
+ }
112
123
 
113
124
  // Auto-detect environment: prefer production mode when dist exists
114
125
  const distPath = join(__dirname, '../dist/index.js');
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@bike4mind/cli",
6
- version: "0.2.34",
6
+ version: "0.2.35-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
7
7
  type: "module",
8
8
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
9
9
  license: "UNLICENSED",
@@ -114,10 +114,10 @@ var package_default = {
114
114
  },
115
115
  devDependencies: {
116
116
  "@bike4mind/agents": "0.1.0",
117
- "@bike4mind/common": "2.56.0",
118
- "@bike4mind/mcp": "1.32.3",
119
- "@bike4mind/services": "2.53.0",
120
- "@bike4mind/utils": "2.9.1",
117
+ "@bike4mind/common": "2.56.1-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
118
+ "@bike4mind/mcp": "1.32.4-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
119
+ "@bike4mind/services": "2.53.1-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
120
+ "@bike4mind/utils": "2.9.2-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
121
121
  "@types/better-sqlite3": "^7.6.13",
122
122
  "@types/diff": "^5.0.9",
123
123
  "@types/jsonwebtoken": "^9.0.4",
@@ -138,7 +138,7 @@ var package_default = {
138
138
  optionalDependencies: {
139
139
  "@vscode/ripgrep": "^1.17.0"
140
140
  },
141
- gitHead: "541fa130ab03ae992ac54caef18c0aa5858ccc16"
141
+ gitHead: "3d2fae3ac3f2b2cd7d12279e4f610b073002b96d"
142
142
  };
143
143
 
144
144
  // src/utils/updateChecker.ts
@@ -398,7 +398,8 @@ var CliConfigSchema = z.object({
398
398
  config: z.record(z.string(), z.any())
399
399
  }),
400
400
  trustedTools: z.array(z.string()).optional().prefault([]),
401
- sandbox: SandboxConfigSchema.optional()
401
+ sandbox: SandboxConfigSchema.optional(),
402
+ additionalDirectories: z.array(z.string()).optional().prefault([])
402
403
  });
403
404
  var ProjectConfigSchema = z.object({
404
405
  tools: z.object({
@@ -418,7 +419,8 @@ var ProjectConfigSchema = z.object({
418
419
  enableSkillTool: z.boolean().optional(),
419
420
  enableDynamicAgentCreation: z.boolean().optional()
420
421
  }).optional(),
421
- sandbox: PartialSandboxConfigSchema
422
+ sandbox: PartialSandboxConfigSchema,
423
+ additionalDirectories: z.array(z.string()).optional()
422
424
  });
423
425
  var ProjectLocalConfigSchema = z.object({
424
426
  trustedTools: z.array(z.string()).optional(),
@@ -465,8 +467,10 @@ var DEFAULT_CONFIG = {
465
467
  // Web-only tools
466
468
  config: {}
467
469
  },
468
- trustedTools: []
470
+ trustedTools: [],
469
471
  // No tools trusted by default
472
+ additionalDirectories: []
473
+ // No additional directories by default
470
474
  };
471
475
  function findProjectConfigDir(startDir = process.cwd()) {
472
476
  let currentDir = startDir;
@@ -1017,6 +1021,53 @@ ${entryToAdd}
1017
1021
  }
1018
1022
  return loadProjectLocalConfig(this.projectConfigDir);
1019
1023
  }
1024
+ /**
1025
+ * Add a directory to the allowed directories list
1026
+ * Persists to global config
1027
+ */
1028
+ async addDirectory(dirPath) {
1029
+ const config = await this.load();
1030
+ if (!config.additionalDirectories) {
1031
+ config.additionalDirectories = [];
1032
+ }
1033
+ const resolvedPath = path2.resolve(dirPath);
1034
+ if (!config.additionalDirectories.includes(resolvedPath)) {
1035
+ config.additionalDirectories.push(resolvedPath);
1036
+ await this.save(config);
1037
+ }
1038
+ }
1039
+ /**
1040
+ * Remove a directory from the allowed directories list
1041
+ */
1042
+ async removeDirectory(dirPath) {
1043
+ const config = await this.load();
1044
+ if (config.additionalDirectories) {
1045
+ const resolvedPath = path2.resolve(dirPath);
1046
+ config.additionalDirectories = config.additionalDirectories.filter((d) => path2.resolve(d) !== resolvedPath);
1047
+ await this.save(config);
1048
+ }
1049
+ }
1050
+ /**
1051
+ * Get all additional directories (merged from global + project configs)
1052
+ * Returns resolved absolute paths
1053
+ */
1054
+ async getAdditionalDirectories() {
1055
+ const config = await this.load();
1056
+ const dirs = /* @__PURE__ */ new Set();
1057
+ if (config.additionalDirectories) {
1058
+ for (const dir of config.additionalDirectories) {
1059
+ dirs.add(path2.resolve(dir));
1060
+ }
1061
+ }
1062
+ const projectConfig = await this.loadRawProjectConfig();
1063
+ if (projectConfig?.additionalDirectories) {
1064
+ const projectRoot = this.projectConfigDir || process.cwd();
1065
+ for (const dir of projectConfig.additionalDirectories) {
1066
+ dirs.add(path2.resolve(projectRoot, dir));
1067
+ }
1068
+ }
1069
+ return Array.from(dirs);
1070
+ }
1020
1071
  };
1021
1072
 
1022
1073
  export {
@@ -3,7 +3,7 @@ import {
3
3
  fetchLatestVersion,
4
4
  forceCheckForUpdate,
5
5
  package_default
6
- } from "../chunk-RGWBOKBB.js";
6
+ } from "../chunk-552ZZZ6B.js";
7
7
 
8
8
  // src/commands/doctorCommand.ts
9
9
  import { execSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigStore
4
- } from "../chunk-ZFVDH4DU.js";
4
+ } from "../chunk-PXLQHH5U.js";
5
5
  import "../chunk-5W4P24VS.js";
6
6
  import "../chunk-4BIBE3J7.js";
7
7
 
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  forceCheckForUpdate,
4
4
  package_default
5
- } from "../chunk-RGWBOKBB.js";
5
+ } from "../chunk-552ZZZ6B.js";
6
6
 
7
7
  // src/commands/updateCommand.ts
8
8
  import { execSync } from "child_process";
package/dist/index.js CHANGED
@@ -25,11 +25,11 @@ import "./chunk-BDQBOLYG.js";
25
25
  import {
26
26
  ConfigStore,
27
27
  logger
28
- } from "./chunk-ZFVDH4DU.js";
28
+ } from "./chunk-PXLQHH5U.js";
29
29
  import {
30
30
  checkForUpdate,
31
31
  package_default
32
- } from "./chunk-RGWBOKBB.js";
32
+ } from "./chunk-552ZZZ6B.js";
33
33
  import {
34
34
  selectActiveBackgroundAgents,
35
35
  useCliStore
@@ -102,6 +102,9 @@ import React22, { useState as useState11, useEffect as useEffect7, useCallback a
102
102
  import { render, Box as Box21, Text as Text21, useApp, useInput as useInput10 } from "ink";
103
103
  import { execSync } from "child_process";
104
104
  import { randomBytes as randomBytes5 } from "crypto";
105
+ import { existsSync as existsSync13, promises as fs15 } from "fs";
106
+ import { homedir as homedir4 } from "os";
107
+ import path20 from "path";
105
108
  import { v4 as uuidv413 } from "uuid";
106
109
 
107
110
  // src/components/App.tsx
@@ -835,6 +838,20 @@ var COMMANDS = [
835
838
  {
836
839
  name: "sandbox:violations:clear",
837
840
  description: "Clear all recorded sandbox violations"
841
+ },
842
+ {
843
+ name: "add-dir",
844
+ description: "Add a directory for file access",
845
+ args: "<path>"
846
+ },
847
+ {
848
+ name: "remove-dir",
849
+ description: "Remove a directory from file access",
850
+ args: "<path>"
851
+ },
852
+ {
853
+ name: "dirs",
854
+ description: "List all accessible directories"
838
855
  }
839
856
  ];
840
857
  function getAllCommandNames() {
@@ -2626,6 +2643,7 @@ import { Box as Box20, Text as Text20 } from "ink";
2626
2643
  import Spinner3 from "ink-spinner";
2627
2644
  import jwt from "jsonwebtoken";
2628
2645
  import open from "open";
2646
+ import axios2 from "axios";
2629
2647
 
2630
2648
  // src/auth/OAuthClient.ts
2631
2649
  import axios from "axios";
@@ -2737,6 +2755,33 @@ var OAuthClient = class {
2737
2755
  };
2738
2756
 
2739
2757
  // src/components/LoginFlow.tsx
2758
+ function extractErrorMessage(err) {
2759
+ if (axios2.isAxiosError(err)) {
2760
+ if (err.response?.data) {
2761
+ const data = err.response.data;
2762
+ const serverMsg = data.error_description || data.error || data.message;
2763
+ if (serverMsg) {
2764
+ return `${serverMsg} (HTTP ${err.response.status})`;
2765
+ }
2766
+ return `Server returned HTTP ${err.response.status}`;
2767
+ }
2768
+ if (err.code === "ECONNREFUSED") {
2769
+ const url = err.config?.baseURL || "server";
2770
+ return `Could not connect to ${url} - is the server running?`;
2771
+ }
2772
+ if (err.code === "ENOTFOUND") {
2773
+ return `Could not resolve hostname: ${err.config?.baseURL || "unknown"}`;
2774
+ }
2775
+ if (err.code === "ETIMEDOUT" || err.code === "ECONNABORTED") {
2776
+ return "Connection timed out - server may be unreachable";
2777
+ }
2778
+ return err.message || `Network error (${err.code || "unknown"})`;
2779
+ }
2780
+ if (err instanceof Error) {
2781
+ return err.message || "Unknown error occurred";
2782
+ }
2783
+ return "Unknown error occurred";
2784
+ }
2740
2785
  function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
2741
2786
  const [status, setStatus] = useState10("initiating");
2742
2787
  const [deviceFlow, setDeviceFlow] = useState10(null);
@@ -2770,9 +2815,9 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
2770
2815
  setTimeout(() => onSuccess(), 1500);
2771
2816
  } catch (err) {
2772
2817
  setStatus("error");
2773
- const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
2818
+ const errorMessage = extractErrorMessage(err);
2774
2819
  setError(errorMessage);
2775
- onError(err instanceof Error ? err : new Error(errorMessage));
2820
+ onError(new Error(errorMessage));
2776
2821
  }
2777
2822
  };
2778
2823
  runLoginFlow();
@@ -4435,6 +4480,7 @@ function buildCoreSystemPrompt(contextSection, config) {
4435
4480
  let agentDirectoryContext = "";
4436
4481
  let skillsSection = "";
4437
4482
  let dynamicAgentSection = "";
4483
+ let directoriesSection = "";
4438
4484
  if (typeof contextSection === "string") {
4439
4485
  projectContextSection = contextSection;
4440
4486
  if (config) {
@@ -4468,6 +4514,21 @@ ${config.contextContent}`;
4468
4514
  if (config.enableDynamicAgentCreation) {
4469
4515
  dynamicAgentSection = buildDynamicAgentPromptSection();
4470
4516
  }
4517
+ if (config.additionalDirectories && config.additionalDirectories.length > 0) {
4518
+ directoriesSection = `
4519
+
4520
+ ## Additional Allowed Directories
4521
+
4522
+ In addition to the working directory (${process.cwd()}), you have read/write access to these directories:
4523
+ ${config.additionalDirectories.map((d) => `- ${d}`).join("\n")}
4524
+
4525
+ To access files in additional directories, pass the full path to the 'dir_path' parameter of file tools:
4526
+ - ${TOOL_GREP_SEARCH}(pattern="...", dir_path="/path/to/additional/dir")
4527
+ - ${TOOL_GLOB_FILES}(pattern="**/*.ts", dir_path="/path/to/additional/dir")
4528
+ - ${TOOL_FILE_READ}(path="/path/to/additional/dir/file.ts")
4529
+
4530
+ When the user asks about content in an additional directory, search there first using the dir_path parameter.`;
4531
+ }
4471
4532
  }
4472
4533
  return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
4473
4534
 
@@ -4557,7 +4618,7 @@ EXAMPLES:
4557
4618
  - "what packages installed?" \u2192 ${TOOL_GLOB_FILES} "**/package.json" \u2192 ${TOOL_FILE_READ}
4558
4619
  - "find all components using React hooks" \u2192 ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
4559
4620
 
4560
- Remember: Use context from previous messages to understand follow-up questions.${projectContextSection}${skillsSection}`;
4621
+ Remember: Use context from previous messages to understand follow-up questions.${directoriesSection}${projectContextSection}${skillsSection}`;
4561
4622
  }
4562
4623
  function buildDynamicAgentPromptSection() {
4563
4624
  return `
@@ -4608,7 +4669,7 @@ You have access to the '${TOOL_CREATE_DYNAMIC_AGENT}' tool, which lets you creat
4608
4669
 
4609
4670
  // src/utils/toolsAdapter.ts
4610
4671
  import { rmSync } from "fs";
4611
- import path15 from "path";
4672
+ import path14 from "path";
4612
4673
 
4613
4674
  // ../../b4m-core/packages/services/dist/src/referService/generateCodes.js
4614
4675
  import { randomBytes } from "crypto";
@@ -5050,7 +5111,7 @@ var incrementUserCounterSchema = z27.object({
5050
5111
  });
5051
5112
 
5052
5113
  // ../../b4m-core/packages/services/dist/src/countersService/sendSlackReport.js
5053
- import axios2 from "axios";
5114
+ import axios3 from "axios";
5054
5115
 
5055
5116
  // ../../b4m-core/packages/services/dist/src/countersService/aiInsights.js
5056
5117
  import OpenAI from "openai";
@@ -5064,7 +5125,7 @@ import OpenAI2 from "openai";
5064
5125
  // ../../b4m-core/packages/services/dist/src/importHistoryService/index.js
5065
5126
  import fs7, { unlinkSync as unlinkSync2 } from "fs";
5066
5127
  import yauzl from "yauzl";
5067
- import axios3 from "axios";
5128
+ import axios4 from "axios";
5068
5129
 
5069
5130
  // ../../b4m-core/packages/services/dist/src/importHistoryService/importOpenaiHistory.js
5070
5131
  import { z as z28 } from "zod";
@@ -6346,7 +6407,7 @@ var researchTaskRetrySchema = z120.object({
6346
6407
  });
6347
6408
 
6348
6409
  // ../../b4m-core/packages/services/dist/src/researchTaskService/processDiscoveredLinks.js
6349
- import axios4 from "axios";
6410
+ import axios5 from "axios";
6350
6411
  import { z as z121 } from "zod";
6351
6412
  import plimit from "p-limit";
6352
6413
  import pLimit2 from "p-limit";
@@ -6357,7 +6418,7 @@ var researchTaskProcessDiscoveredLinksSchema = z121.object({
6357
6418
  // ../../b4m-core/packages/services/dist/src/researchTaskService/downloadRelevantLinks.js
6358
6419
  import { z as z122 } from "zod";
6359
6420
  import plimit2 from "p-limit";
6360
- import axios5 from "axios";
6421
+ import axios6 from "axios";
6361
6422
  import { fileTypeFromBuffer } from "file-type";
6362
6423
  var researchTaskDownloadRelevantLinksSchema = z122.object({
6363
6424
  id: z122.string()
@@ -6736,7 +6797,7 @@ var weatherTool = {
6736
6797
  };
6737
6798
 
6738
6799
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/imageGeneration/index.js
6739
- import axios6 from "axios";
6800
+ import axios7 from "axios";
6740
6801
  import { fileTypeFromBuffer as fileTypeFromBuffer2 } from "file-type";
6741
6802
  import { v4 as uuidv45 } from "uuid";
6742
6803
  async function downloadImage(url) {
@@ -6744,7 +6805,7 @@ async function downloadImage(url) {
6744
6805
  const base64Data = url.split(",")[1];
6745
6806
  return Buffer.from(base64Data, "base64");
6746
6807
  }
6747
- const response = await axios6.get(url, { responseType: "arraybuffer" });
6808
+ const response = await axios7.get(url, { responseType: "arraybuffer" });
6748
6809
  return response.data;
6749
6810
  }
6750
6811
  async function processAndStoreImages(images, context) {
@@ -8135,7 +8196,7 @@ Return only the edited content without any markdown code blocks or explanations.
8135
8196
  };
8136
8197
 
8137
8198
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/imageEdit/index.js
8138
- import axios7 from "axios";
8199
+ import axios8 from "axios";
8139
8200
  import { fileTypeFromBuffer as fileTypeFromBuffer3 } from "file-type";
8140
8201
  import { v4 as uuidv46 } from "uuid";
8141
8202
  var EDIT_SUPPORTED_MODELS = [
@@ -8152,10 +8213,10 @@ async function downloadImage2(url) {
8152
8213
  return Buffer.from(base64Data, "base64");
8153
8214
  }
8154
8215
  try {
8155
- const response = await axios7.get(url, { responseType: "arraybuffer", timeout: 3e4 });
8216
+ const response = await axios8.get(url, { responseType: "arraybuffer", timeout: 3e4 });
8156
8217
  return response.data;
8157
8218
  } catch (error) {
8158
- if (axios7.isAxiosError(error)) {
8219
+ if (axios8.isAxiosError(error)) {
8159
8220
  if (error.response?.status === 403 || error.response?.status === 404) {
8160
8221
  throw new Error(`Image URL is expired or inaccessible. Please use a file ID from the workbench or a recently generated image URL. Original error: ${error.message}`);
8161
8222
  }
@@ -8313,7 +8374,7 @@ Please select a supported edit model in your image settings modal.`;
8313
8374
  } catch (error) {
8314
8375
  console.error("[ERROR] BFL image editing failed:", error);
8315
8376
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
8316
- if (axios7.isAxiosError(error)) {
8377
+ if (axios8.isAxiosError(error)) {
8317
8378
  const status = error.response?.status;
8318
8379
  const responseData = error.response?.data;
8319
8380
  if (status === 403) {
@@ -8353,7 +8414,7 @@ Please check your BFL API key in settings and ensure it is configured correctly.
8353
8414
  } catch (error) {
8354
8415
  console.error("[ERROR] Gemini image editing failed:", error);
8355
8416
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
8356
- if (axios7.isAxiosError(error)) {
8417
+ if (axios8.isAxiosError(error)) {
8357
8418
  const status = error.response?.status;
8358
8419
  const responseData = error.response?.data;
8359
8420
  if (status === 403 || status === 401) {
@@ -8387,7 +8448,7 @@ Please check your BFL API key in settings and ensure it is configured correctly.
8387
8448
  } catch (error) {
8388
8449
  console.error("[ERROR] OpenAI image editing failed:", error);
8389
8450
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
8390
- if (axios7.isAxiosError(error)) {
8451
+ if (axios8.isAxiosError(error)) {
8391
8452
  const status = error.response?.status;
8392
8453
  const responseData = error.response?.data;
8393
8454
  if (status === 403 || status === 401) {
@@ -10134,16 +10195,50 @@ var planetVisibilityTool = {
10134
10195
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
10135
10196
  import { promises as fs8 } from "fs";
10136
10197
  import { existsSync as existsSync5, statSync as statSync4 } from "fs";
10198
+
10199
+ // ../../b4m-core/packages/services/dist/src/llm/tools/utils/pathValidation.js
10137
10200
  import path8 from "path";
10138
- var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
10139
- async function readFileContent(params) {
10140
- const { path: filePath, encoding = "utf-8", offset = 0, limit } = params;
10201
+ import { realpathSync as realpathSync2 } from "fs";
10202
+ function resolveRealPath(filePath) {
10203
+ try {
10204
+ return realpathSync2(filePath);
10205
+ } catch {
10206
+ const parentDir = path8.dirname(filePath);
10207
+ const basename2 = path8.basename(filePath);
10208
+ if (parentDir === filePath) {
10209
+ return filePath;
10210
+ }
10211
+ return path8.join(resolveRealPath(parentDir), basename2);
10212
+ }
10213
+ }
10214
+ function isPathAllowed(filePath, allowedDirectories) {
10215
+ const cwd = resolveRealPath(process.cwd());
10216
+ const allAllowed = [cwd, ...(allowedDirectories || []).map((d) => resolveRealPath(d))];
10141
10217
  const normalizedPath = path8.normalize(filePath);
10142
- const resolvedPath = path8.resolve(process.cwd(), normalizedPath);
10143
- const cwd = path8.resolve(process.cwd());
10144
- if (!resolvedPath.startsWith(cwd)) {
10145
- throw new Error(`Access denied: Cannot read files outside of current working directory`);
10218
+ const logicalPath = path8.isAbsolute(normalizedPath) ? normalizedPath : path8.resolve(cwd, normalizedPath);
10219
+ const resolvedPath = resolveRealPath(logicalPath);
10220
+ for (const dir of allAllowed) {
10221
+ if (resolvedPath === dir || resolvedPath.startsWith(dir + path8.sep)) {
10222
+ return { allowed: true, resolvedPath, matchedDirectory: dir };
10223
+ }
10146
10224
  }
10225
+ return { allowed: false, resolvedPath };
10226
+ }
10227
+ function assertPathAllowed(filePath, allowedDirectories, operation = "access") {
10228
+ const result = isPathAllowed(filePath, allowedDirectories);
10229
+ if (!result.allowed) {
10230
+ const cwd = process.cwd();
10231
+ const dirsMsg = allowedDirectories && allowedDirectories.length > 0 ? `Allowed directories: ${[cwd, ...allowedDirectories].join(", ")}` : `Working directory: ${cwd}`;
10232
+ throw new Error(`Access denied: Cannot ${operation} files outside allowed directories. ${dirsMsg}`);
10233
+ }
10234
+ return result.resolvedPath;
10235
+ }
10236
+
10237
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
10238
+ var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
10239
+ async function readFileContent(params, allowedDirectories) {
10240
+ const { path: filePath, encoding = "utf-8", offset = 0, limit } = params;
10241
+ const resolvedPath = assertPathAllowed(filePath, allowedDirectories, "read");
10147
10242
  if (!existsSync5(resolvedPath)) {
10148
10243
  throw new Error(`File not found: ${filePath}`);
10149
10244
  }
@@ -10212,8 +10307,9 @@ var fileReadTool = {
10212
10307
  const params = value;
10213
10308
  context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
10214
10309
  try {
10215
- const content = await readFileContent(params);
10216
- const stats = statSync4(path8.resolve(process.cwd(), path8.normalize(params.path)));
10310
+ const content = await readFileContent(params, context.allowedDirectories);
10311
+ const { resolvedPath: validatedPath } = isPathAllowed(params.path, context.allowedDirectories);
10312
+ const stats = statSync4(validatedPath);
10217
10313
  context.logger.info("\u2705 FileRead: Success", {
10218
10314
  path: params.path,
10219
10315
  size: stats.size,
@@ -10260,14 +10356,9 @@ var fileReadTool = {
10260
10356
  import { promises as fs9 } from "fs";
10261
10357
  import { existsSync as existsSync6 } from "fs";
10262
10358
  import path9 from "path";
10263
- async function createFile(params) {
10359
+ async function createFile(params, allowedDirectories) {
10264
10360
  const { path: filePath, content, createDirectories = true } = params;
10265
- const normalizedPath = path9.normalize(filePath);
10266
- const resolvedPath = path9.resolve(process.cwd(), normalizedPath);
10267
- const cwd = path9.resolve(process.cwd());
10268
- if (!resolvedPath.startsWith(cwd)) {
10269
- throw new Error(`Access denied: Cannot create files outside of current working directory`);
10270
- }
10361
+ const resolvedPath = assertPathAllowed(filePath, allowedDirectories, "create");
10271
10362
  const fileExists = existsSync6(resolvedPath);
10272
10363
  const action = fileExists ? "overwritten" : "created";
10273
10364
  if (createDirectories) {
@@ -10292,7 +10383,7 @@ var createFileTool = {
10292
10383
  size: params.content.length
10293
10384
  });
10294
10385
  try {
10295
- const result = await createFile(params);
10386
+ const result = await createFile(params, context.allowedDirectories);
10296
10387
  context.logger.info("\u2705 CreateFile: Success", { path: params.path });
10297
10388
  return result;
10298
10389
  } catch (error) {
@@ -10341,12 +10432,14 @@ var DEFAULT_IGNORE_PATTERNS = [
10341
10432
  "**/*.min.js",
10342
10433
  "**/*.min.css"
10343
10434
  ];
10344
- async function findFiles(params) {
10435
+ async function findFiles(params, allowedDirectories) {
10345
10436
  const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
10346
10437
  const baseCwd = process.cwd();
10347
10438
  const targetDir = dir_path ? path10.resolve(baseCwd, path10.normalize(dir_path)) : baseCwd;
10348
- if (!targetDir.startsWith(baseCwd)) {
10349
- throw new Error(`Access denied: Cannot search outside of current working directory`);
10439
+ const validation = isPathAllowed(targetDir, allowedDirectories);
10440
+ if (!validation.allowed) {
10441
+ const dirsMsg = allowedDirectories && allowedDirectories.length > 0 ? `Allowed directories: ${[baseCwd, ...allowedDirectories].join(", ")}` : `Working directory: ${baseCwd}`;
10442
+ throw new Error(`Access denied: Cannot search outside allowed directories. ${dirsMsg}`);
10350
10443
  }
10351
10444
  const ignorePatterns = respect_git_ignore ? DEFAULT_IGNORE_PATTERNS : [];
10352
10445
  const matches = await glob(pattern, {
@@ -10399,7 +10492,7 @@ var globFilesTool = {
10399
10492
  dir_path: params.dir_path || "."
10400
10493
  });
10401
10494
  try {
10402
- const result = await findFiles(params);
10495
+ const result = await findFiles(params, context.allowedDirectories);
10403
10496
  context.logger.info("\u2705 GlobFiles: Success");
10404
10497
  return result;
10405
10498
  } catch (error) {
@@ -10475,12 +10568,6 @@ function getRipgrepPath() {
10475
10568
  throw new Error("ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services");
10476
10569
  }
10477
10570
  }
10478
- function isPathWithinWorkspace(targetPath, baseCwd) {
10479
- const resolvedTarget = path11.resolve(targetPath);
10480
- const resolvedBase = path11.resolve(baseCwd);
10481
- const relativePath = path11.relative(resolvedBase, resolvedTarget);
10482
- return !relativePath.startsWith("..") && !path11.isAbsolute(relativePath);
10483
- }
10484
10571
  function convertGlobToRipgrepGlobs(globPattern) {
10485
10572
  if (!globPattern || globPattern === "**/*") {
10486
10573
  return [];
@@ -10492,12 +10579,14 @@ function convertGlobToRipgrepGlobs(globPattern) {
10492
10579
  }
10493
10580
  return [globPattern];
10494
10581
  }
10495
- async function searchFiles2(params) {
10582
+ async function searchFiles2(params, allowedDirectories) {
10496
10583
  const { pattern, dir_path, include } = params;
10497
10584
  const baseCwd = process.cwd();
10498
10585
  const targetDir = dir_path ? path11.resolve(baseCwd, dir_path) : baseCwd;
10499
- if (!isPathWithinWorkspace(targetDir, baseCwd)) {
10500
- throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
10586
+ const validation = isPathAllowed(targetDir, allowedDirectories);
10587
+ if (!validation.allowed) {
10588
+ const dirsMsg = allowedDirectories && allowedDirectories.length > 0 ? `Allowed directories: ${[baseCwd, ...allowedDirectories].join(", ")}` : `Working directory: ${baseCwd}`;
10589
+ throw new Error(`Path validation failed: "${dir_path}" resolves outside allowed directories. ${dirsMsg}`);
10501
10590
  }
10502
10591
  try {
10503
10592
  const stats = await stat2(targetDir);
@@ -10616,7 +10705,7 @@ var grepSearchTool = {
10616
10705
  include: params.include || "**/*"
10617
10706
  });
10618
10707
  try {
10619
- const result = await searchFiles2(params);
10708
+ const result = await searchFiles2(params, context.allowedDirectories);
10620
10709
  context.logger.info("\u2705 GrepSearch: Success");
10621
10710
  return result;
10622
10711
  } catch (error) {
@@ -10653,14 +10742,9 @@ var grepSearchTool = {
10653
10742
  import { promises as fs10 } from "fs";
10654
10743
  import { existsSync as existsSync8, statSync as statSync5 } from "fs";
10655
10744
  import path12 from "path";
10656
- async function deleteFile(params) {
10745
+ async function deleteFile(params, allowedDirectories) {
10657
10746
  const { path: filePath, recursive = false } = params;
10658
- const normalizedPath = path12.normalize(filePath);
10659
- const resolvedPath = path12.resolve(process.cwd(), normalizedPath);
10660
- const cwd = path12.resolve(process.cwd());
10661
- if (!resolvedPath.startsWith(cwd)) {
10662
- throw new Error(`Access denied: Cannot delete files outside of current working directory`);
10663
- }
10747
+ const resolvedPath = assertPathAllowed(filePath, allowedDirectories, "delete");
10664
10748
  if (!existsSync8(resolvedPath)) {
10665
10749
  throw new Error(`File or directory not found: ${filePath}`);
10666
10750
  }
@@ -10691,7 +10775,7 @@ var deleteFileTool = {
10691
10775
  recursive: params.recursive
10692
10776
  });
10693
10777
  try {
10694
- const result = await deleteFile(params);
10778
+ const result = await deleteFile(params, context.allowedDirectories);
10695
10779
  context.logger.info("\u2705 DeleteFile: Success", { path: params.path });
10696
10780
  return result;
10697
10781
  } catch (error) {
@@ -14555,7 +14639,6 @@ function parseQuery(query) {
14555
14639
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/editLocalFile/index.js
14556
14640
  import { promises as fs11 } from "fs";
14557
14641
  import { existsSync as existsSync9 } from "fs";
14558
- import path14 from "path";
14559
14642
  import { diffLines as diffLines3 } from "diff";
14560
14643
  function generateDiff(original, modified) {
14561
14644
  const differences = diffLines3(original, modified);
@@ -14577,14 +14660,9 @@ function generateDiff(original, modified) {
14577
14660
  });
14578
14661
  return { additions, deletions, diff: diffString.trim() };
14579
14662
  }
14580
- async function editLocalFile(params) {
14663
+ async function editLocalFile(params, allowedDirectories) {
14581
14664
  const { path: filePath, old_string, new_string } = params;
14582
- const normalizedPath = path14.normalize(filePath);
14583
- const resolvedPath = path14.resolve(process.cwd(), normalizedPath);
14584
- const cwd = path14.resolve(process.cwd());
14585
- if (!resolvedPath.startsWith(cwd)) {
14586
- throw new Error(`Access denied: Cannot edit files outside of current working directory`);
14587
- }
14665
+ const resolvedPath = assertPathAllowed(filePath, allowedDirectories, "edit");
14588
14666
  if (!existsSync9(resolvedPath)) {
14589
14667
  throw new Error(`File not found: ${filePath}`);
14590
14668
  }
@@ -14617,7 +14695,7 @@ var editLocalFileTool = {
14617
14695
  newStringLength: params.new_string.length
14618
14696
  });
14619
14697
  try {
14620
- const result = await editLocalFile(params);
14698
+ const result = await editLocalFile(params, context.allowedDirectories);
14621
14699
  context.logger.info("\u2705 EditLocalFile: Success", { path: params.path });
14622
14700
  return result;
14623
14701
  } catch (error) {
@@ -15170,7 +15248,7 @@ var cliOnlyTools = {
15170
15248
  // Interactive tools
15171
15249
  ask_user_question: askUserQuestionTool
15172
15250
  };
15173
- var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools = b4mTools) => {
15251
+ var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools = b4mTools, allowedDirectories) => {
15174
15252
  const context = {
15175
15253
  userId,
15176
15254
  user,
@@ -15183,7 +15261,8 @@ var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorag
15183
15261
  onFinish,
15184
15262
  llm,
15185
15263
  model,
15186
- imageProcessorLambdaName
15264
+ imageProcessorLambdaName,
15265
+ allowedDirectories
15187
15266
  };
15188
15267
  return Object.entries(tools).reduce((acc, [key, tool]) => ({
15189
15268
  ...acc,
@@ -16041,7 +16120,7 @@ import { fromZodError } from "zod-validation-error";
16041
16120
  var BUILT_IN_TOOL_SET = new Set(b4mLLMTools.options);
16042
16121
 
16043
16122
  // ../../b4m-core/packages/services/dist/src/llm/ImageGeneration.js
16044
- import axios8 from "axios";
16123
+ import axios9 from "axios";
16045
16124
  import { fileTypeFromBuffer as fileTypeFromBuffer4 } from "file-type";
16046
16125
  import { v4 as uuidv47 } from "uuid";
16047
16126
  import { z as z141 } from "zod";
@@ -16063,7 +16142,7 @@ var ImageGenerationBodySchema = OpenAIImageGenerationInput.extend({
16063
16142
  });
16064
16143
 
16065
16144
  // ../../b4m-core/packages/services/dist/src/llm/VideoGeneration.js
16066
- import axios9 from "axios";
16145
+ import axios10 from "axios";
16067
16146
  import { v4 as uuidv48 } from "uuid";
16068
16147
  import { z as z142 } from "zod";
16069
16148
  import { fromZodError as fromZodError3 } from "zod-validation-error";
@@ -16079,7 +16158,7 @@ var VideoGenerationBodySchema = z142.object({
16079
16158
  });
16080
16159
 
16081
16160
  // ../../b4m-core/packages/services/dist/src/llm/ImageEdit.js
16082
- import axios10 from "axios";
16161
+ import axios11 from "axios";
16083
16162
  import { fileTypeFromBuffer as fileTypeFromBuffer5 } from "file-type";
16084
16163
  import { v4 as uuidv49 } from "uuid";
16085
16164
  import { z as z143 } from "zod";
@@ -16189,7 +16268,7 @@ async function generateFileDeletePreview(args) {
16189
16268
  if (!existsSync10(args.path)) {
16190
16269
  return `[File does not exist: ${args.path}]`;
16191
16270
  }
16192
- const stats = await import("fs/promises").then((fs15) => fs15.stat(args.path));
16271
+ const stats = await import("fs/promises").then((fs16) => fs16.stat(args.path));
16193
16272
  return `[File will be deleted]
16194
16273
 
16195
16274
  Path: ${args.path}
@@ -16538,7 +16617,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
16538
16617
  let isSandboxed = false;
16539
16618
  let sandboxedArgs = args;
16540
16619
  if (toolName === "bash_execute" && args?.command && sandboxOrchestrator) {
16541
- const cwd = args.cwd ? path15.resolve(process.cwd(), args.cwd) : process.cwd();
16620
+ const cwd = args.cwd ? path14.resolve(process.cwd(), args.cwd) : process.cwd();
16542
16621
  const decision = sandboxOrchestrator.shouldSandbox(args.command, cwd);
16543
16622
  if (decision.type === "blocked") {
16544
16623
  sandboxOrchestrator.recordBlocked();
@@ -16784,7 +16863,7 @@ function wrapToolWithCheckpointing(tool, checkpointStore) {
16784
16863
  const filePath = args?.path;
16785
16864
  if (filePath) {
16786
16865
  try {
16787
- await checkpointStore.createCheckpoint(toolName, [filePath], `before-${toolName}-${path15.basename(filePath)}`);
16866
+ await checkpointStore.createCheckpoint(toolName, [filePath], `before-${toolName}-${path14.basename(filePath)}`);
16788
16867
  } catch {
16789
16868
  }
16790
16869
  }
@@ -16816,7 +16895,7 @@ function normalizeToolName(toolName) {
16816
16895
  }
16817
16896
  return TOOL_NAME_MAPPING[toolName] || toolName;
16818
16897
  }
16819
- function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter, showUserQuestion, checkpointStore, sandboxOrchestrator) {
16898
+ function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter, showUserQuestion, checkpointStore, sandboxOrchestrator, allowedDirectories) {
16820
16899
  if (showUserQuestion) {
16821
16900
  setShowUserQuestionFn(showUserQuestion);
16822
16901
  }
@@ -16876,7 +16955,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
16876
16955
  model,
16877
16956
  void 0,
16878
16957
  // imageProcessorLambdaName (not needed for CLI)
16879
- tools_to_generate
16958
+ tools_to_generate,
16959
+ allowedDirectories
16880
16960
  );
16881
16961
  let tools = Object.entries(toolsMap).map(([_, tool]) => {
16882
16962
  const permissionWrapped = wrapToolWithPermission(
@@ -17087,7 +17167,7 @@ function getEnvironmentName(configApiConfig) {
17087
17167
 
17088
17168
  // src/utils/contextLoader.ts
17089
17169
  import * as fs12 from "fs";
17090
- import * as path16 from "path";
17170
+ import * as path15 from "path";
17091
17171
  import { homedir as homedir3 } from "os";
17092
17172
  var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
17093
17173
  var PROJECT_CONTEXT_FILES = [
@@ -17108,7 +17188,7 @@ function formatFileSize2(bytes) {
17108
17188
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
17109
17189
  }
17110
17190
  function tryReadContextFile(dir, filename, source) {
17111
- const filePath = path16.join(dir, filename);
17191
+ const filePath = path15.join(dir, filename);
17112
17192
  try {
17113
17193
  const stats = fs12.lstatSync(filePath);
17114
17194
  if (stats.isDirectory()) {
@@ -17176,7 +17256,7 @@ ${project.content}`;
17176
17256
  }
17177
17257
  async function loadContextFiles(projectDir) {
17178
17258
  const errors = [];
17179
- const globalDir = path16.join(homedir3(), ".bike4mind");
17259
+ const globalDir = path15.join(homedir3(), ".bike4mind");
17180
17260
  const projectDirectory = projectDir || process.cwd();
17181
17261
  const [globalResult, projectResult] = await Promise.all([
17182
17262
  Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
@@ -17447,7 +17527,7 @@ function substituteArguments(template, args) {
17447
17527
  // ../../b4m-core/packages/mcp/dist/src/client.js
17448
17528
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
17449
17529
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
17450
- import path17 from "path";
17530
+ import path16 from "path";
17451
17531
  import { existsSync as existsSync11, readdirSync as readdirSync3 } from "fs";
17452
17532
  var MCPClient = class {
17453
17533
  // Note: This class handles MCP server communication with repository filtering
@@ -17489,16 +17569,16 @@ var MCPClient = class {
17489
17569
  const root = process.env.INIT_CWD || process.cwd();
17490
17570
  const candidatePaths = [
17491
17571
  // When running from SST Lambda with node_modules structure (copyFiles)
17492
- path17.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
17572
+ path16.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
17493
17573
  // When running from SST Lambda deployed environment (/var/task)
17494
- path17.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17574
+ path16.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17495
17575
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
17496
- path17.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17576
+ path16.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17497
17577
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
17498
- path17.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17578
+ path16.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17499
17579
  // Original paths (backward compatibility)
17500
- path17.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17501
- path17.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
17580
+ path16.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
17581
+ path16.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
17502
17582
  ];
17503
17583
  const serverScriptPath = candidatePaths.find((p) => existsSync11(p));
17504
17584
  if (!serverScriptPath) {
@@ -18842,12 +18922,12 @@ var WebSocketToolExecutor = class {
18842
18922
  };
18843
18923
 
18844
18924
  // src/auth/ApiClient.ts
18845
- import axios11 from "axios";
18925
+ import axios12 from "axios";
18846
18926
  var ApiClient = class {
18847
18927
  constructor(baseURL = "http://localhost:3000", configStore) {
18848
18928
  this.configStore = configStore || new ConfigStore();
18849
18929
  this.oauthClient = new OAuthClient(baseURL);
18850
- this.client = axios11.create({
18930
+ this.client = axios12.create({
18851
18931
  baseURL,
18852
18932
  headers: {
18853
18933
  "Content-Type": "application/json"
@@ -19445,7 +19525,7 @@ var SubagentOrchestrator = class {
19445
19525
 
19446
19526
  // src/agents/AgentStore.ts
19447
19527
  import fs13 from "fs/promises";
19448
- import path18 from "path";
19528
+ import path17 from "path";
19449
19529
  import os2 from "os";
19450
19530
  import matter2 from "gray-matter";
19451
19531
  var FULL_MODEL_ID_PREFIXES = [
@@ -19595,10 +19675,10 @@ var AgentStore = class {
19595
19675
  const root = projectRoot || process.cwd();
19596
19676
  const home = os2.homedir();
19597
19677
  this.builtinAgentsDir = builtinDir;
19598
- this.globalB4MAgentsDir = path18.join(home, ".bike4mind", "agents");
19599
- this.globalClaudeAgentsDir = path18.join(home, ".claude", "agents");
19600
- this.projectB4MAgentsDir = path18.join(root, ".bike4mind", "agents");
19601
- this.projectClaudeAgentsDir = path18.join(root, ".claude", "agents");
19678
+ this.globalB4MAgentsDir = path17.join(home, ".bike4mind", "agents");
19679
+ this.globalClaudeAgentsDir = path17.join(home, ".claude", "agents");
19680
+ this.projectB4MAgentsDir = path17.join(root, ".bike4mind", "agents");
19681
+ this.projectClaudeAgentsDir = path17.join(root, ".claude", "agents");
19602
19682
  }
19603
19683
  /**
19604
19684
  * Load all agents from all directories
@@ -19652,7 +19732,7 @@ var AgentStore = class {
19652
19732
  try {
19653
19733
  const entries = await fs13.readdir(directory, { withFileTypes: true });
19654
19734
  for (const entry of entries) {
19655
- const fullPath = path18.join(directory, entry.name);
19735
+ const fullPath = path17.join(directory, entry.name);
19656
19736
  if (entry.isDirectory()) {
19657
19737
  const subFiles = await this.findAgentFiles(fullPath);
19658
19738
  files.push(...subFiles);
@@ -19672,7 +19752,7 @@ var AgentStore = class {
19672
19752
  const content = await fs13.readFile(filePath, "utf-8");
19673
19753
  const { data: frontmatter, content: body } = matter2(content);
19674
19754
  const parsed = AgentFrontmatterSchema.parse(frontmatter);
19675
- const name = path18.basename(filePath, ".md");
19755
+ const name = path17.basename(filePath, ".md");
19676
19756
  const modelInput = parsed.model || DEFAULT_AGENT_MODEL;
19677
19757
  const resolution = resolveModelAlias(modelInput, name, filePath);
19678
19758
  if (!resolution.resolved && resolution.warning) {
@@ -19753,7 +19833,7 @@ var AgentStore = class {
19753
19833
  */
19754
19834
  async createAgentFile(name, isGlobal = false, useClaude = true) {
19755
19835
  const targetDir = isGlobal ? useClaude ? this.globalClaudeAgentsDir : this.globalB4MAgentsDir : useClaude ? this.projectClaudeAgentsDir : this.projectB4MAgentsDir;
19756
- const filePath = path18.join(targetDir, `${name}.md`);
19836
+ const filePath = path17.join(targetDir, `${name}.md`);
19757
19837
  try {
19758
19838
  await fs13.access(filePath);
19759
19839
  throw new Error(`Agent file already exists: ${filePath}`);
@@ -20582,7 +20662,7 @@ function createTodoStore(onUpdate) {
20582
20662
 
20583
20663
  // src/tools/findDefinitionTool.ts
20584
20664
  import { stat as stat3 } from "fs/promises";
20585
- import path19 from "path";
20665
+ import path18 from "path";
20586
20666
  import { execFile as execFile2 } from "child_process";
20587
20667
  import { promisify as promisify2 } from "util";
20588
20668
  import { createRequire as createRequire2 } from "module";
@@ -20603,19 +20683,19 @@ var ALL_KEYWORDS = Object.values(KIND_KEYWORDS).flat();
20603
20683
  function getRipgrepPath2() {
20604
20684
  try {
20605
20685
  const ripgrepPath = require3.resolve("@vscode/ripgrep");
20606
- const ripgrepDir = path19.dirname(ripgrepPath);
20607
- return path19.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
20686
+ const ripgrepDir = path18.dirname(ripgrepPath);
20687
+ return path18.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
20608
20688
  } catch {
20609
20689
  throw new Error(
20610
20690
  "ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services"
20611
20691
  );
20612
20692
  }
20613
20693
  }
20614
- function isPathWithinWorkspace2(targetPath, baseCwd) {
20615
- const resolvedTarget = path19.resolve(targetPath);
20616
- const resolvedBase = path19.resolve(baseCwd);
20617
- const relativePath = path19.relative(resolvedBase, resolvedTarget);
20618
- return !relativePath.startsWith("..") && !path19.isAbsolute(relativePath);
20694
+ function isPathWithinWorkspace(targetPath, baseCwd) {
20695
+ const resolvedTarget = path18.resolve(targetPath);
20696
+ const resolvedBase = path18.resolve(baseCwd);
20697
+ const relativePath = path18.relative(resolvedBase, resolvedTarget);
20698
+ return !relativePath.startsWith("..") && !path18.isAbsolute(relativePath);
20619
20699
  }
20620
20700
  function escapeRegex(str) {
20621
20701
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -20648,8 +20728,8 @@ async function findDefinitions(params) {
20648
20728
  throw new Error("symbol_name is required");
20649
20729
  }
20650
20730
  const baseCwd = process.cwd();
20651
- const targetDir = search_path ? path19.resolve(baseCwd, search_path) : baseCwd;
20652
- if (!isPathWithinWorkspace2(targetDir, baseCwd)) {
20731
+ const targetDir = search_path ? path18.resolve(baseCwd, search_path) : baseCwd;
20732
+ if (!isPathWithinWorkspace(targetDir, baseCwd)) {
20653
20733
  throw new Error(`Path validation failed: "${search_path}" resolves outside the allowed workspace directory`);
20654
20734
  }
20655
20735
  try {
@@ -20703,7 +20783,7 @@ async function findDefinitions(params) {
20703
20783
  const lineText = match.data.lines.text.trimEnd();
20704
20784
  if (isLikelyDefinition(lineText)) {
20705
20785
  allMatches.push({
20706
- filePath: path19.relative(targetDir, match.data.path.text) || path19.basename(match.data.path.text),
20786
+ filePath: path18.relative(targetDir, match.data.path.text) || path18.basename(match.data.path.text),
20707
20787
  lineNumber: match.data.line_number,
20708
20788
  line: lineText
20709
20789
  });
@@ -20782,7 +20862,7 @@ function createFindDefinitionTool() {
20782
20862
 
20783
20863
  // src/tools/getFileStructure/index.ts
20784
20864
  import { existsSync as existsSync12, promises as fs14, statSync as statSync6 } from "fs";
20785
- import path20 from "path";
20865
+ import path19 from "path";
20786
20866
 
20787
20867
  // src/tools/getFileStructure/formatter.ts
20788
20868
  var SECTIONS = [
@@ -20826,7 +20906,7 @@ function createGetFileStructureTool() {
20826
20906
  const params = value;
20827
20907
  try {
20828
20908
  const cwd = process.cwd();
20829
- const resolvedPath = path20.resolve(cwd, params.path);
20909
+ const resolvedPath = path19.resolve(cwd, params.path);
20830
20910
  if (!resolvedPath.startsWith(cwd)) {
20831
20911
  return "Error: Access denied - cannot read files outside of current working directory";
20832
20912
  }
@@ -20840,7 +20920,7 @@ function createGetFileStructureTool() {
20840
20920
  if (stats.size > MAX_FILE_SIZE4) {
20841
20921
  return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE4 / 1024 / 1024}MB`;
20842
20922
  }
20843
- const ext = path20.extname(resolvedPath).toLowerCase();
20923
+ const ext = path19.extname(resolvedPath).toLowerCase();
20844
20924
  const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-4SGFQDY3.js");
20845
20925
  const languageId = getLanguageForExtension(ext);
20846
20926
  if (!languageId) {
@@ -20911,7 +20991,8 @@ function CliApp() {
20911
20991
  backgroundManager: null,
20912
20992
  sandboxOrchestrator: null,
20913
20993
  wsManager: null,
20914
- checkpointStore: null
20994
+ checkpointStore: null,
20995
+ additionalDirectories: []
20915
20996
  });
20916
20997
  const [isInitialized, setIsInitialized] = useState11(false);
20917
20998
  const [initError, setInitError] = useState11(null);
@@ -21005,6 +21086,9 @@ function CliApp() {
21005
21086
  try {
21006
21087
  const startupLog = [];
21007
21088
  const config = await state.configStore.load();
21089
+ const configDirs = await state.configStore.getAdditionalDirectories();
21090
+ const flagDirs = process.env.B4M_ADDITIONAL_DIRS ? JSON.parse(process.env.B4M_ADDITIONAL_DIRS) : [];
21091
+ const additionalDirectories = [.../* @__PURE__ */ new Set([...configDirs, ...flagDirs])];
21008
21092
  const history = await state.commandHistoryStore.load();
21009
21093
  setCommandHistory(history);
21010
21094
  try {
@@ -21243,7 +21327,8 @@ function CliApp() {
21243
21327
  // toolFilter
21244
21328
  userQuestionFn,
21245
21329
  checkpointStore,
21246
- sandboxOrchestrator
21330
+ sandboxOrchestrator,
21331
+ additionalDirectories
21247
21332
  );
21248
21333
  startupLog.push(`\u{1F6E0}\uFE0F Loaded ${b4mTools2.length} B4M tool(s)`);
21249
21334
  const mcpManager = new McpManager(config);
@@ -21315,6 +21400,9 @@ function CliApp() {
21315
21400
  }
21316
21401
  const allTools = [...b4mTools2, ...mcpTools, ...cliTools];
21317
21402
  startupLog.push(`\u{1F4C2} Working directory: ${process.cwd()}`);
21403
+ if (additionalDirectories.length > 0) {
21404
+ startupLog.push(`\u{1F4C1} Additional directories: ${additionalDirectories.length}`);
21405
+ }
21318
21406
  const agentNamesList = agentStore.getAgentNames().join(", ");
21319
21407
  startupLog.push(`\u{1F916} Subagent delegation enabled (${agentNamesList}) + background execution`);
21320
21408
  if (skillTool) {
@@ -21345,7 +21433,8 @@ function CliApp() {
21345
21433
  agentStore,
21346
21434
  customCommands: state.customCommandStore.getAllCommands(),
21347
21435
  enableSkillTool,
21348
- enableDynamicAgentCreation: config.preferences.enableDynamicAgentCreation === true
21436
+ enableDynamicAgentCreation: config.preferences.enableDynamicAgentCreation === true,
21437
+ additionalDirectories
21349
21438
  });
21350
21439
  const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
21351
21440
  const agent = new ReActAgent({
@@ -21411,8 +21500,10 @@ function CliApp() {
21411
21500
  // Store sandbox orchestrator for /sandbox commands
21412
21501
  wsManager,
21413
21502
  // WebSocket connection manager (null if using SSE fallback)
21414
- checkpointStore
21503
+ checkpointStore,
21415
21504
  // File change checkpointing for undo/restore
21505
+ additionalDirectories
21506
+ // Store additional directories for file access
21416
21507
  }));
21417
21508
  setStoreSession(newSession);
21418
21509
  const bannerLines = [
@@ -21688,7 +21779,8 @@ function CliApp() {
21688
21779
  contextContent: state.contextContent,
21689
21780
  agentStore: state.agentStore || void 0,
21690
21781
  customCommands: state.customCommandStore.getAllCommands(),
21691
- enableSkillTool: config?.preferences.enableSkillTool !== false
21782
+ enableSkillTool: config?.preferences.enableSkillTool !== false,
21783
+ additionalDirectories: state.additionalDirectories
21692
21784
  });
21693
21785
  const contextUsage = tokenCounter2.countSessionTokens(activeSession, systemPrompt);
21694
21786
  if (contextUsage.totalTokens >= threshold) {
@@ -22730,7 +22822,8 @@ No usage data available for the last ${USAGE_DAYS} days.`);
22730
22822
  contextContent: state.contextContent,
22731
22823
  agentStore: state.agentStore || void 0,
22732
22824
  customCommands: commands,
22733
- enableSkillTool: state.config?.preferences.enableSkillTool !== false
22825
+ enableSkillTool: state.config?.preferences.enableSkillTool !== false,
22826
+ additionalDirectories: state.additionalDirectories
22734
22827
  });
22735
22828
  const usage = tokenCounter2.countSessionTokens(state.session, systemPrompt);
22736
22829
  const totalWithTools = usage.totalTokens + mcpToolsTokens;
@@ -23164,6 +23257,76 @@ Allowed domains (${domains.length}):`);
23164
23257
  console.log("");
23165
23258
  break;
23166
23259
  }
23260
+ case "add-dir": {
23261
+ let dirPath = args.join(" ").trim();
23262
+ if (!dirPath) {
23263
+ console.log("Usage: /add-dir <path>");
23264
+ console.log("\nExample:");
23265
+ console.log(" /add-dir /path/to/directory");
23266
+ console.log(" /add-dir ~/codes/my-project");
23267
+ break;
23268
+ }
23269
+ if (dirPath.startsWith("~/")) {
23270
+ dirPath = path20.join(homedir4(), dirPath.slice(2));
23271
+ } else if (dirPath === "~") {
23272
+ dirPath = homedir4();
23273
+ }
23274
+ const resolvedPath = path20.resolve(dirPath);
23275
+ if (!existsSync13(resolvedPath)) {
23276
+ console.log(`\u274C Directory does not exist: ${resolvedPath}`);
23277
+ break;
23278
+ }
23279
+ const dirStat = await fs15.stat(resolvedPath);
23280
+ if (!dirStat.isDirectory()) {
23281
+ console.log(`\u274C Path is not a directory: ${resolvedPath}`);
23282
+ break;
23283
+ }
23284
+ await state.configStore.addDirectory(resolvedPath);
23285
+ setState((prev) => ({
23286
+ ...prev,
23287
+ additionalDirectories: [...prev.additionalDirectories || [], resolvedPath]
23288
+ }));
23289
+ console.log(`\u2705 Added directory: ${resolvedPath}`);
23290
+ console.log(" File tools now have access to this directory.");
23291
+ break;
23292
+ }
23293
+ case "remove-dir": {
23294
+ let removeDir = args.join(" ").trim();
23295
+ if (!removeDir) {
23296
+ console.log("Usage: /remove-dir <path>");
23297
+ break;
23298
+ }
23299
+ if (removeDir.startsWith("~/")) {
23300
+ removeDir = path20.join(homedir4(), removeDir.slice(2));
23301
+ } else if (removeDir === "~") {
23302
+ removeDir = homedir4();
23303
+ }
23304
+ const resolvedRemovePath = path20.resolve(removeDir);
23305
+ await state.configStore.removeDirectory(resolvedRemovePath);
23306
+ setState((prev) => ({
23307
+ ...prev,
23308
+ additionalDirectories: (prev.additionalDirectories || []).filter((d) => path20.resolve(d) !== resolvedRemovePath)
23309
+ }));
23310
+ console.log(`\u2705 Removed directory: ${resolvedRemovePath}`);
23311
+ break;
23312
+ }
23313
+ case "dirs": {
23314
+ const additionalDirs = await state.configStore.getAdditionalDirectories();
23315
+ const cwd = process.cwd();
23316
+ console.log("\n\u{1F4C2} Accessible Directories:\n");
23317
+ console.log(` \u{1F4C1} Working directory: ${cwd}`);
23318
+ if (additionalDirs.length > 0) {
23319
+ console.log("\n \u{1F4C1} Additional directories:");
23320
+ additionalDirs.forEach((d) => {
23321
+ console.log(` ${d}`);
23322
+ });
23323
+ } else {
23324
+ console.log("\n No additional directories configured.");
23325
+ console.log(" Use /add-dir <path> or --add-dir <path> flag to add directories.");
23326
+ }
23327
+ console.log("");
23328
+ break;
23329
+ }
23167
23330
  case "sandbox:violations": {
23168
23331
  const vStore = state.sandboxOrchestrator?.getViolationStore();
23169
23332
  if (!vStore) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.2.34",
3
+ "version": "0.2.35-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -111,10 +111,10 @@
111
111
  },
112
112
  "devDependencies": {
113
113
  "@bike4mind/agents": "0.1.0",
114
- "@bike4mind/common": "2.56.0",
115
- "@bike4mind/mcp": "1.32.3",
116
- "@bike4mind/services": "2.53.0",
117
- "@bike4mind/utils": "2.9.1",
114
+ "@bike4mind/common": "2.56.1-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
115
+ "@bike4mind/mcp": "1.32.4-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
116
+ "@bike4mind/services": "2.53.1-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
117
+ "@bike4mind/utils": "2.9.2-fix-reply-disappears-on-refresh.20009+3d2fae3ac",
118
118
  "@types/better-sqlite3": "^7.6.13",
119
119
  "@types/diff": "^5.0.9",
120
120
  "@types/jsonwebtoken": "^9.0.4",
@@ -135,5 +135,5 @@
135
135
  "optionalDependencies": {
136
136
  "@vscode/ripgrep": "^1.17.0"
137
137
  },
138
- "gitHead": "541fa130ab03ae992ac54caef18c0aa5858ccc16"
138
+ "gitHead": "3d2fae3ac3f2b2cd7d12279e4f610b073002b96d"
139
139
  }