@bike4mind/cli 0.2.8-feat-cli-session-resume.17148 → 0.2.10-dowgrade-mcp-sdk.17228

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/README.md CHANGED
@@ -10,6 +10,7 @@ Interactive command-line interface for Bike4Mind with ReAct agents.
10
10
  - 🛠️ B4M tools + MCP integration
11
11
  - 🎨 Rich terminal UI with Ink
12
12
  - 🐛 Debug logging with `--verbose` flag
13
+ - 📄 Context file loading (CLAUDE.md, AGENTS.md, AI.md)
13
14
 
14
15
  ## Installation
15
16
 
@@ -160,6 +161,7 @@ export SERPER_API_KEY="your-key-here"
160
161
  - ✅ `math_evaluate` - No API key needed
161
162
  - ✅ `current_datetime` - No API key needed
162
163
  - ✅ `prompt_enhancement` - No API key needed
164
+ - ✅ `bash_execute` - No API key needed
163
165
  - 🔑 `weather_info` - Requires `toolApiKeys.openweather`
164
166
  - 🔑 `web_search` - Requires `toolApiKeys.serper`
165
167
  - 🔑 `deep_research` - Requires `toolApiKeys.serper`
@@ -241,6 +243,51 @@ You can also run any MCP server via npx or custom executables:
241
243
 
242
244
  **Note:** Internal MCP servers must be built and available in the `b4m-core/packages/mcp/dist/src/` directory. The CLI will automatically find them if you're running from the monorepo. For Docker-based servers, ensure Docker is installed and the image is accessible.
243
245
 
246
+ ## Context Files
247
+
248
+ The CLI supports loading project-specific instructions from context files, similar to CLAUDE.md in Claude Code. These files provide persistent instructions that are automatically included in the agent's system prompt.
249
+
250
+ ### Supported Files
251
+
252
+ **Project-level** (in your project directory, checked in priority order):
253
+ 1. `CLAUDE.local.md` - Personal/gitignored instructions (highest priority)
254
+ 2. `CLAUDE.md` - Project instructions
255
+ 3. `AGENTS.md` - Cross-tool standard
256
+ 4. `AI.local.md` - Personal/gitignored instructions
257
+ 5. `AI.md` - Project instructions
258
+ 6. `INSTRUCTIONS.md` - Project instructions (lowest priority)
259
+
260
+ **Global** (in `~/.bike4mind/`):
261
+ 1. `AI.local.md` - Personal global instructions
262
+ 2. `AI.md` - Global instructions
263
+
264
+ ### How It Works
265
+
266
+ - The CLI loads the **first matching file** from each layer (global and project)
267
+ - Project files take precedence over global files
268
+ - `.local.md` variants are intended to be gitignored for personal preferences
269
+ - Files must be under 100KB
270
+ - Symlinks are rejected for security
271
+
272
+ ### Example
273
+
274
+ Create a `CLAUDE.md` in your project:
275
+
276
+ ```markdown
277
+ # Project Instructions
278
+
279
+ - Always use TypeScript strict mode
280
+ - Prefer functional components with hooks
281
+ - Run tests before committing
282
+ ```
283
+
284
+ On startup, you'll see:
285
+ ```
286
+ 📄 Project context: CLAUDE.md
287
+ ```
288
+
289
+ The agent will follow these instructions for all interactions in that project.
290
+
244
291
  ## Session Storage
245
292
 
246
293
  Sessions are saved to `~/.bike4mind/sessions/`
@@ -338,6 +385,7 @@ Current implementation includes:
338
385
  ✅ B4M tools integration
339
386
  ✅ MCP tools support
340
387
  ✅ Debug logging with --verbose flag
388
+ ✅ Context file loading (CLAUDE.md, AGENTS.md, AI.md)
341
389
 
342
390
  Coming soon:
343
391
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CurationArtifactType
4
- } from "./chunk-H6SHASMZ.js";
4
+ } from "./chunk-DJPXSSP4.js";
5
5
  import "./chunk-PDX44BCA.js";
6
6
 
7
7
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/artifactExtractor.js
@@ -7,11 +7,11 @@ import {
7
7
  getSettingsMap,
8
8
  getSettingsValue,
9
9
  secureParameters
10
- } from "./chunk-DNSBC3LO.js";
10
+ } from "./chunk-GCVIRGIN.js";
11
11
  import {
12
12
  KnowledgeType,
13
13
  SupportedFabFileMimeTypes
14
- } from "./chunk-H6SHASMZ.js";
14
+ } from "./chunk-DJPXSSP4.js";
15
15
 
16
16
  // ../../b4m-core/packages/services/dist/src/fabFileService/create.js
17
17
  import { z } from "zod";
@@ -230,7 +230,9 @@ var b4mLLMTools = z3.enum([
230
230
  "create_file",
231
231
  "glob_files",
232
232
  "grep_search",
233
- "delete_file"
233
+ "delete_file",
234
+ // Shell execution
235
+ "bash_execute"
234
236
  ]);
235
237
  var B4MLLMToolsList = b4mLLMTools.options.map((tool) => tool);
236
238
  var RechartsChartTypeSchema = z3.enum([
@@ -4038,6 +4040,15 @@ var UserApiKeyEvents;
4038
4040
  UserApiKeyEvents2["DELETED"] = "User API Key Deleted";
4039
4041
  })(UserApiKeyEvents || (UserApiKeyEvents = {}));
4040
4042
 
4043
+ // ../../b4m-core/packages/common/dist/src/schemas/analytics/events/slack.js
4044
+ var SlackEvents;
4045
+ (function(SlackEvents2) {
4046
+ SlackEvents2["SLACK_COMMAND_RECEIVED"] = "Slack Command Received";
4047
+ SlackEvents2["SLACK_COMMAND_COMPLETED"] = "Slack Command Completed";
4048
+ SlackEvents2["SLACK_MCP_TOOL_INVOKED"] = "Slack MCP Tool Invoked";
4049
+ SlackEvents2["SLACK_BULK_OPERATION"] = "Slack Bulk Operation Executed";
4050
+ })(SlackEvents || (SlackEvents = {}));
4051
+
4041
4052
  // ../../b4m-core/packages/common/dist/src/schemas/team.js
4042
4053
  import { z as z22 } from "zod";
4043
4054
  var InternalTeamMemberSchema = z22.object({
@@ -6114,6 +6125,7 @@ export {
6114
6125
  FriendshipEvents,
6115
6126
  OrganizationEvents,
6116
6127
  UserApiKeyEvents,
6128
+ SlackEvents,
6117
6129
  InternalTeamMemberSchema,
6118
6130
  InternalTeamMemberListSchema,
6119
6131
  PREDEFINED_USER_TAGS,
@@ -16,7 +16,7 @@ import {
16
16
  dayjsConfig_default,
17
17
  extractSnippetMeta,
18
18
  settingsMap
19
- } from "./chunk-H6SHASMZ.js";
19
+ } from "./chunk-DJPXSSP4.js";
20
20
 
21
21
  // ../../b4m-core/packages/utils/dist/src/storage/S3Storage.js
22
22
  import { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand } from "@aws-sdk/client-s3";
@@ -6,12 +6,12 @@ import {
6
6
  getSettingsByNames,
7
7
  obfuscateApiKey,
8
8
  secureParameters
9
- } from "./chunk-DNSBC3LO.js";
9
+ } from "./chunk-GCVIRGIN.js";
10
10
  import {
11
11
  ApiKeyType,
12
12
  MementoTier,
13
13
  isSupportedEmbeddingModel
14
- } from "./chunk-H6SHASMZ.js";
14
+ } from "./chunk-DJPXSSP4.js";
15
15
 
16
16
  // ../../b4m-core/packages/services/dist/src/apiKeyService/get.js
17
17
  import { z } from "zod";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  BadRequestError,
4
4
  secureParameters
5
- } from "./chunk-DNSBC3LO.js";
5
+ } from "./chunk-GCVIRGIN.js";
6
6
  import {
7
7
  GenericCreditDeductTransaction,
8
8
  ImageEditUsageTransaction,
@@ -10,7 +10,7 @@ import {
10
10
  RealtimeVoiceUsageTransaction,
11
11
  TextGenerationUsageTransaction,
12
12
  TransferCreditTransaction
13
- } from "./chunk-H6SHASMZ.js";
13
+ } from "./chunk-DJPXSSP4.js";
14
14
 
15
15
  // ../../b4m-core/packages/services/dist/src/creditService/subtractCredits.js
16
16
  import { z } from "zod";
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  createFabFile,
4
4
  createFabFileSchema
5
- } from "./chunk-YXHQN7SK.js";
6
- import "./chunk-DNSBC3LO.js";
5
+ } from "./chunk-AQBZVAYO.js";
6
+ import "./chunk-GCVIRGIN.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
- import "./chunk-H6SHASMZ.js";
8
+ import "./chunk-DJPXSSP4.js";
9
9
  import "./chunk-PDX44BCA.js";
10
10
  export {
11
11
  createFabFile,
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-3CCBDIFR.js";
8
- import "./chunk-LIRY24M5.js";
9
- import "./chunk-YXHQN7SK.js";
7
+ } from "./chunk-GO75FMLY.js";
8
+ import "./chunk-VZU4Z7WI.js";
9
+ import "./chunk-AQBZVAYO.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-DNSBC3LO.js";
18
+ } from "./chunk-GCVIRGIN.js";
19
19
  import {
20
20
  Logger
21
21
  } from "./chunk-AMDXHL6S.js";
@@ -73,7 +73,7 @@ import {
73
73
  XAI_IMAGE_MODELS,
74
74
  b4mLLMTools,
75
75
  getMcpProviderMetadata
76
- } from "./chunk-H6SHASMZ.js";
76
+ } from "./chunk-DJPXSSP4.js";
77
77
  import {
78
78
  __require
79
79
  } from "./chunk-PDX44BCA.js";
@@ -85,7 +85,7 @@ import { v4 as uuidv49 } from "uuid";
85
85
 
86
86
  // src/components/App.tsx
87
87
  import React10 from "react";
88
- import { Box as Box9, Text as Text9, Static } from "ink";
88
+ import { Box as Box9, Text as Text9 } from "ink";
89
89
 
90
90
  // src/components/StatusBar.tsx
91
91
  import React from "react";
@@ -742,6 +742,7 @@ function InputPrompt({
742
742
  const [selectedIndex, setSelectedIndex] = useState2(0);
743
743
  const [historyIndex, setHistoryIndex] = useState2(-1);
744
744
  const [tempInput, setTempInput] = useState2("");
745
+ const [inputKey, setInputKey] = useState2(0);
745
746
  useEffect(() => {
746
747
  if (prefillInput) {
747
748
  setValue(prefillInput);
@@ -889,10 +890,11 @@ function InputPrompt({
889
890
  return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(
890
891
  CustomTextInput,
891
892
  {
893
+ key: inputKey,
892
894
  value,
893
895
  onChange: handleChange,
894
896
  onSubmit: handleSubmit,
895
- placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
897
+ placeholder: disabled ? "" : "Type your message, /help for commands, or @file to reference",
896
898
  showCursor: !disabled
897
899
  }
898
900
  )), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
@@ -923,6 +925,7 @@ var useCliStore = create((set) => ({
923
925
  addAgentStep: (step) => set((state) => ({
924
926
  agentSteps: [...state.agentSteps, step]
925
927
  })),
928
+ setAgentSteps: (steps) => set({ agentSteps: steps }),
926
929
  clearAgentSteps: () => set({ agentSteps: [] }),
927
930
  // Permission prompt
928
931
  permissionPrompt: null,
@@ -942,7 +945,7 @@ var AgentThinking = React7.memo(function AgentThinking2() {
942
945
  if (!isThinking) {
943
946
  return null;
944
947
  }
945
- return /* @__PURE__ */ React7.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
948
+ return /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
946
949
  });
947
950
 
948
951
  // src/components/PermissionPrompt.tsx
@@ -1397,7 +1400,7 @@ ${errorBlock}`;
1397
1400
  canBeTrusted: permissionPrompt.canBeTrusted,
1398
1401
  onResponse: onPermissionResponse
1399
1402
  }
1400
- )) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(MessageItem, { message }))), /* @__PURE__ */ React10.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
1403
+ )) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, messages.map((message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: `${message.timestamp}-${message.role}-${index}`, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(MessageItem, { message })))), /* @__PURE__ */ React10.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
1401
1404
  InputPrompt,
1402
1405
  {
1403
1406
  onSubmit: handleSubmit,
@@ -1410,7 +1413,8 @@ ${errorBlock}`;
1410
1413
  ))));
1411
1414
  }
1412
1415
  function MessageItem({ message }) {
1413
- return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1416
+ const isUser = message.role === "user";
1417
+ return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1414
1418
  if (step.type === "thought") {
1415
1419
  return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
1416
1420
  }
@@ -1422,7 +1426,7 @@ function MessageItem({ message }) {
1422
1426
  return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
1423
1427
  }
1424
1428
  return null;
1425
- }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1429
+ }).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1426
1430
  }
1427
1431
 
1428
1432
  // src/components/MessageList.tsx
@@ -2688,10 +2692,9 @@ ${options.context}` : this.getSystemPrompt()
2688
2692
  content: query
2689
2693
  }
2690
2694
  ];
2691
- console.log("[DEBUG ReActAgent] Messages array being sent to LLM:");
2692
2695
  messages.forEach((msg, i) => {
2693
2696
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
2694
- console.log(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
2697
+ this.context.logger.debug(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
2695
2698
  });
2696
2699
  let finalAnswer = "";
2697
2700
  let reachedMaxIterations = false;
@@ -5084,8 +5087,8 @@ async function processAndStoreImages(images, context) {
5084
5087
  const buffer = await downloadImage(image);
5085
5088
  const fileType = await fileTypeFromBuffer2(buffer);
5086
5089
  const filename = `${uuidv45()}.${fileType?.ext}`;
5087
- const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
5088
- return path13;
5090
+ const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
5091
+ return path15;
5089
5092
  }));
5090
5093
  }
5091
5094
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -6297,8 +6300,8 @@ async function processAndStoreImage(imageUrl, context) {
6297
6300
  const buffer = await downloadImage2(imageUrl);
6298
6301
  const fileType = await fileTypeFromBuffer3(buffer);
6299
6302
  const filename = `${uuidv46()}.${fileType?.ext}`;
6300
- const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
6301
- return path13;
6303
+ const path15 = await context.imageGenerateStorage.upload(buffer, filename, {});
6304
+ return path15;
6302
6305
  }
6303
6306
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
6304
6307
  await context.onFinish?.("edit_image", storedImagePath);
@@ -8710,6 +8713,381 @@ var deleteFileTool = {
8710
8713
  })
8711
8714
  };
8712
8715
 
8716
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
8717
+ import { spawn } from "child_process";
8718
+ import path11 from "path";
8719
+ var DEFAULT_TIMEOUT_MS = 6e4;
8720
+ var MAX_OUTPUT_SIZE = 100 * 1024;
8721
+ var DANGEROUS_PATTERNS = [
8722
+ // Destructive file operations
8723
+ {
8724
+ pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
8725
+ reason: "Recursive delete with path",
8726
+ block: true
8727
+ },
8728
+ {
8729
+ pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
8730
+ reason: "Force delete without preserve root",
8731
+ block: true
8732
+ },
8733
+ {
8734
+ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
8735
+ reason: "Recursive force delete on root paths",
8736
+ block: true
8737
+ },
8738
+ {
8739
+ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
8740
+ reason: "Force recursive delete on root paths",
8741
+ block: true
8742
+ },
8743
+ // System-level dangerous commands
8744
+ { pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
8745
+ { pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
8746
+ { pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
8747
+ { pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
8748
+ // Disk/partition operations
8749
+ { pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
8750
+ { pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
8751
+ { pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
8752
+ // Network attacks
8753
+ { pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
8754
+ { pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
8755
+ { pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
8756
+ // Fork bombs and resource exhaustion
8757
+ { pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
8758
+ { pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
8759
+ // Credential/sensitive data access
8760
+ { pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
8761
+ { pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
8762
+ { pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
8763
+ // History/log manipulation
8764
+ { pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
8765
+ { pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
8766
+ // Dangerous redirects
8767
+ { pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
8768
+ { pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
8769
+ // Environment manipulation
8770
+ { pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
8771
+ { pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
8772
+ // Process/system control
8773
+ { pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
8774
+ { pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
8775
+ { pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
8776
+ { pattern: /\breboot\b/i, reason: "System reboot", block: true },
8777
+ { pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
8778
+ ];
8779
+ var SAFE_COMMAND_PREFIXES = [
8780
+ "ls",
8781
+ "cat",
8782
+ "head",
8783
+ "tail",
8784
+ "grep",
8785
+ "find",
8786
+ "echo",
8787
+ "pwd",
8788
+ "whoami",
8789
+ "date",
8790
+ "cal",
8791
+ "wc",
8792
+ "sort",
8793
+ "uniq",
8794
+ "cut",
8795
+ "tr",
8796
+ "sed",
8797
+ "awk",
8798
+ "git",
8799
+ "npm",
8800
+ "pnpm",
8801
+ "yarn",
8802
+ "node",
8803
+ "npx",
8804
+ "tsx",
8805
+ "ts-node",
8806
+ "python",
8807
+ "python3",
8808
+ "pip",
8809
+ "pip3",
8810
+ "docker",
8811
+ "docker-compose",
8812
+ "curl",
8813
+ "wget",
8814
+ // These are safe for fetching, blocked when piped to shell
8815
+ "mkdir",
8816
+ "touch",
8817
+ "cp",
8818
+ "mv",
8819
+ // File operations (still require permission)
8820
+ "which",
8821
+ "whereis",
8822
+ "type",
8823
+ "file",
8824
+ "stat",
8825
+ "env",
8826
+ "printenv",
8827
+ "set",
8828
+ "man",
8829
+ "help",
8830
+ "info",
8831
+ "diff",
8832
+ "comm",
8833
+ "cmp",
8834
+ "tar",
8835
+ "zip",
8836
+ "unzip",
8837
+ "gzip",
8838
+ "gunzip",
8839
+ "ssh",
8840
+ "scp",
8841
+ "rsync",
8842
+ // Network tools
8843
+ "make",
8844
+ "cmake",
8845
+ "cargo",
8846
+ "go",
8847
+ "rustc",
8848
+ "gcc",
8849
+ "g++",
8850
+ // Build tools
8851
+ "jest",
8852
+ "vitest",
8853
+ "mocha",
8854
+ "pytest",
8855
+ // Test runners
8856
+ "eslint",
8857
+ "prettier",
8858
+ "tsc"
8859
+ // Linters/formatters
8860
+ ];
8861
+ function checkDangerousPatterns(command) {
8862
+ for (const { pattern, reason, block } of DANGEROUS_PATTERNS) {
8863
+ if (pattern.test(command)) {
8864
+ return { blocked: block, reason };
8865
+ }
8866
+ }
8867
+ return { blocked: false };
8868
+ }
8869
+ function getBaseCommand(command) {
8870
+ const trimmed = command.trim();
8871
+ const match = trimmed.match(/^(\S+)/);
8872
+ return match ? match[1] : "";
8873
+ }
8874
+ function isSafeCommandPrefix(command) {
8875
+ const baseCommand = getBaseCommand(command);
8876
+ return SAFE_COMMAND_PREFIXES.some((safe) => baseCommand === safe || baseCommand.endsWith(`/${safe}`));
8877
+ }
8878
+ async function executeBashCommand(params) {
8879
+ const { command, cwd: relativeCwd, timeout = DEFAULT_TIMEOUT_MS } = params;
8880
+ if (!command || command.trim().length === 0) {
8881
+ return {
8882
+ stdout: "",
8883
+ stderr: "Error: Command cannot be empty",
8884
+ exitCode: 1,
8885
+ timedOut: false,
8886
+ blocked: true,
8887
+ blockedReason: "Empty command"
8888
+ };
8889
+ }
8890
+ const dangerCheck = checkDangerousPatterns(command);
8891
+ if (dangerCheck.blocked) {
8892
+ return {
8893
+ stdout: "",
8894
+ stderr: `Command blocked for safety: ${dangerCheck.reason}`,
8895
+ exitCode: 1,
8896
+ timedOut: false,
8897
+ blocked: true,
8898
+ blockedReason: dangerCheck.reason
8899
+ };
8900
+ }
8901
+ const baseCwd = process.cwd();
8902
+ const targetCwd = relativeCwd ? path11.resolve(baseCwd, relativeCwd) : baseCwd;
8903
+ const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
8904
+ return new Promise((resolve3) => {
8905
+ let stdout = "";
8906
+ let stderr = "";
8907
+ let timedOut = false;
8908
+ const proc = spawn("bash", ["-c", command], {
8909
+ cwd: targetCwd,
8910
+ env: {
8911
+ ...process.env,
8912
+ // Prevent color codes that might be hard to read
8913
+ NO_COLOR: "1",
8914
+ FORCE_COLOR: "0"
8915
+ },
8916
+ stdio: ["ignore", "pipe", "pipe"]
8917
+ });
8918
+ const timeoutId = setTimeout(() => {
8919
+ timedOut = true;
8920
+ proc.kill("SIGTERM");
8921
+ setTimeout(() => {
8922
+ if (!proc.killed) {
8923
+ proc.kill("SIGKILL");
8924
+ }
8925
+ }, 5e3);
8926
+ }, effectiveTimeout);
8927
+ proc.stdout.on("data", (data) => {
8928
+ if (stdout.length < MAX_OUTPUT_SIZE) {
8929
+ stdout += data.toString();
8930
+ if (stdout.length > MAX_OUTPUT_SIZE) {
8931
+ stdout = stdout.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
8932
+ }
8933
+ }
8934
+ });
8935
+ proc.stderr.on("data", (data) => {
8936
+ if (stderr.length < MAX_OUTPUT_SIZE) {
8937
+ stderr += data.toString();
8938
+ if (stderr.length > MAX_OUTPUT_SIZE) {
8939
+ stderr = stderr.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
8940
+ }
8941
+ }
8942
+ });
8943
+ proc.on("close", (exitCode) => {
8944
+ clearTimeout(timeoutId);
8945
+ resolve3({
8946
+ stdout: stdout.trim(),
8947
+ stderr: stderr.trim(),
8948
+ exitCode,
8949
+ timedOut,
8950
+ blocked: false
8951
+ });
8952
+ });
8953
+ proc.on("error", (error) => {
8954
+ clearTimeout(timeoutId);
8955
+ resolve3({
8956
+ stdout: "",
8957
+ stderr: `Failed to execute command: ${error.message}`,
8958
+ exitCode: 1,
8959
+ timedOut: false,
8960
+ blocked: false
8961
+ });
8962
+ });
8963
+ });
8964
+ }
8965
+ function formatResult(result, command) {
8966
+ const parts = [];
8967
+ parts.push(`$ ${command}`);
8968
+ parts.push("");
8969
+ if (result.blocked) {
8970
+ parts.push(`BLOCKED: ${result.blockedReason}`);
8971
+ parts.push("");
8972
+ parts.push("This command was blocked for safety reasons.");
8973
+ parts.push("If you believe this is a false positive, please run the command manually.");
8974
+ return parts.join("\n");
8975
+ }
8976
+ if (result.timedOut) {
8977
+ parts.push("WARNING: Command timed out and was terminated.");
8978
+ parts.push("");
8979
+ }
8980
+ if (result.stdout) {
8981
+ parts.push(result.stdout);
8982
+ }
8983
+ if (result.stderr) {
8984
+ if (result.stdout) {
8985
+ parts.push("");
8986
+ }
8987
+ parts.push("STDERR:");
8988
+ parts.push(result.stderr);
8989
+ }
8990
+ if (result.exitCode !== 0 && result.exitCode !== null) {
8991
+ parts.push("");
8992
+ parts.push(`Exit code: ${result.exitCode}`);
8993
+ }
8994
+ if (!result.stdout && !result.stderr && !result.timedOut) {
8995
+ parts.push("(command completed with no output)");
8996
+ }
8997
+ return parts.join("\n");
8998
+ }
8999
+ var bashExecuteTool = {
9000
+ name: "bash_execute",
9001
+ implementation: (context) => ({
9002
+ toolFn: async (value) => {
9003
+ const params = value;
9004
+ const isSafe = isSafeCommandPrefix(params.command);
9005
+ context.logger.info("Bash: Executing command", {
9006
+ command: params.command,
9007
+ cwd: params.cwd || ".",
9008
+ timeout: params.timeout || DEFAULT_TIMEOUT_MS,
9009
+ isSafeCommand: isSafe
9010
+ });
9011
+ if (context.onStart) {
9012
+ await context.onStart("bash_execute", {
9013
+ command: params.command,
9014
+ cwd: params.cwd
9015
+ });
9016
+ }
9017
+ try {
9018
+ const result = await executeBashCommand(params);
9019
+ const formattedResult = formatResult(result, params.command);
9020
+ context.logger.info("Bash: Command completed", {
9021
+ exitCode: result.exitCode,
9022
+ timedOut: result.timedOut,
9023
+ blocked: result.blocked
9024
+ });
9025
+ if (context.onFinish) {
9026
+ await context.onFinish("bash_execute", {
9027
+ command: params.command,
9028
+ exitCode: result.exitCode,
9029
+ blocked: result.blocked
9030
+ });
9031
+ }
9032
+ return formattedResult;
9033
+ } catch (error) {
9034
+ context.logger.error("Bash: Command failed", error);
9035
+ if (context.onFinish) {
9036
+ await context.onFinish("bash_execute", {
9037
+ command: params.command,
9038
+ error: error instanceof Error ? error.message : "Unknown error"
9039
+ });
9040
+ }
9041
+ throw error;
9042
+ }
9043
+ },
9044
+ toolSchema: {
9045
+ name: "bash_execute",
9046
+ description: `Execute a bash command in the terminal. Use this for running shell commands, scripts, build tools, git operations, and other CLI tasks.
9047
+
9048
+ SAFETY NOTES:
9049
+ - Commands are executed in the current working directory (or specified cwd)
9050
+ - Dangerous commands (sudo, rm -rf /, etc.) are automatically blocked
9051
+ - Commands have a default timeout of 60 seconds (max 5 minutes)
9052
+ - Output is limited to prevent overwhelming responses
9053
+ - This tool ALWAYS requires user permission before execution
9054
+
9055
+ COMMON USE CASES:
9056
+ - Running build commands: npm run build, make, cargo build
9057
+ - Git operations: git status, git log, git diff
9058
+ - Viewing system info: ls, pwd, cat, head, tail
9059
+ - Running tests: npm test, pytest, cargo test
9060
+ - Package management: npm install, pip install
9061
+ - File operations: mkdir, cp, mv (with permission)
9062
+
9063
+ BLOCKED OPERATIONS:
9064
+ - sudo and privilege escalation
9065
+ - Recursive deletes on system paths
9066
+ - Direct disk operations
9067
+ - Fork bombs and resource exhaustion
9068
+ - Piping remote scripts to shell`,
9069
+ parameters: {
9070
+ type: "object",
9071
+ properties: {
9072
+ command: {
9073
+ type: "string",
9074
+ description: "The bash command to execute. Can include pipes, redirects, and chained commands."
9075
+ },
9076
+ cwd: {
9077
+ type: "string",
9078
+ description: "Working directory for the command (optional). Can be relative or absolute path. Defaults to current directory."
9079
+ },
9080
+ timeout: {
9081
+ type: "number",
9082
+ description: "Timeout in milliseconds (optional, default: 60000, max: 300000). Command will be terminated if it exceeds this time."
9083
+ }
9084
+ },
9085
+ required: ["command"]
9086
+ }
9087
+ }
9088
+ })
9089
+ };
9090
+
8713
9091
  // ../../b4m-core/packages/services/dist/src/llm/tools/index.js
8714
9092
  var tools = {
8715
9093
  dice_roll: diceRollTool,
@@ -8737,7 +9115,9 @@ var tools = {
8737
9115
  create_file: createFileTool,
8738
9116
  glob_files: globFilesTool,
8739
9117
  grep_search: grepSearchTool,
8740
- delete_file: deleteFileTool
9118
+ delete_file: deleteFileTool,
9119
+ // Shell execution
9120
+ bash_execute: bashExecuteTool
8741
9121
  };
8742
9122
  var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
8743
9123
  const context = {
@@ -9012,7 +9392,7 @@ async function generateFileDeletePreview(args) {
9012
9392
  if (!existsSync7(args.path)) {
9013
9393
  return `[File does not exist: ${args.path}]`;
9014
9394
  }
9015
- const stats = await import("fs/promises").then((fs11) => fs11.stat(args.path));
9395
+ const stats = await import("fs/promises").then((fs12) => fs12.stat(args.path));
9016
9396
  return `[File will be deleted]
9017
9397
 
9018
9398
  Path: ${args.path}
@@ -9025,7 +9405,7 @@ Last modified: ${stats.mtime.toLocaleString()}`;
9025
9405
 
9026
9406
  // src/utils/Logger.ts
9027
9407
  import fs10 from "fs/promises";
9028
- import path11 from "path";
9408
+ import path12 from "path";
9029
9409
  import os from "os";
9030
9410
  var Logger2 = class _Logger {
9031
9411
  constructor() {
@@ -9048,9 +9428,9 @@ var Logger2 = class _Logger {
9048
9428
  */
9049
9429
  async initialize(sessionId) {
9050
9430
  this.sessionId = sessionId;
9051
- const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9431
+ const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
9052
9432
  await fs10.mkdir(debugDir, { recursive: true });
9053
- this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
9433
+ this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
9054
9434
  await this.writeToFile("INFO", "=== CLI SESSION START ===");
9055
9435
  }
9056
9436
  /**
@@ -9260,12 +9640,12 @@ var Logger2 = class _Logger {
9260
9640
  async cleanupOldLogs() {
9261
9641
  if (!this.fileLoggingEnabled) return;
9262
9642
  try {
9263
- const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9643
+ const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
9264
9644
  const files = await fs10.readdir(debugDir);
9265
9645
  const now = Date.now();
9266
9646
  const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
9267
9647
  for (const file of files) {
9268
- const filePath = path11.join(debugDir, file);
9648
+ const filePath = path12.join(debugDir, file);
9269
9649
  const stats = await fs10.stat(filePath);
9270
9650
  if (stats.mtime.getTime() < thirtyDaysAgo) {
9271
9651
  await fs10.unlink(filePath);
@@ -9330,7 +9710,8 @@ var LOCAL_TOOLS = [
9330
9710
  "edit_file",
9331
9711
  "dice_roll",
9332
9712
  "math_evaluate",
9333
- "current_datetime"
9713
+ "current_datetime",
9714
+ "bash_execute"
9334
9715
  ];
9335
9716
  function isServerTool(toolName) {
9336
9717
  return SERVER_TOOLS.includes(toolName);
@@ -9360,21 +9741,21 @@ var NoOpStorage = class extends BaseStorage {
9360
9741
  async upload(input, destination, options) {
9361
9742
  return `/tmp/${destination}`;
9362
9743
  }
9363
- async download(path13) {
9744
+ async download(path15) {
9364
9745
  throw new Error("Download not supported in CLI");
9365
9746
  }
9366
- async delete(path13) {
9747
+ async delete(path15) {
9367
9748
  }
9368
- async getSignedUrl(path13) {
9369
- return `/tmp/${path13}`;
9749
+ async getSignedUrl(path15) {
9750
+ return `/tmp/${path15}`;
9370
9751
  }
9371
- getPublicUrl(path13) {
9372
- return `/tmp/${path13}`;
9752
+ getPublicUrl(path15) {
9753
+ return `/tmp/${path15}`;
9373
9754
  }
9374
- async getPreview(path13) {
9375
- return `/tmp/${path13}`;
9755
+ async getPreview(path15) {
9756
+ return `/tmp/${path15}`;
9376
9757
  }
9377
- async getMetadata(path13) {
9758
+ async getMetadata(path15) {
9378
9759
  return { size: 0, contentType: "application/octet-stream" };
9379
9760
  }
9380
9761
  };
@@ -9432,6 +9813,10 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
9432
9813
  } catch (error) {
9433
9814
  preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9434
9815
  }
9816
+ } else if (toolName === "bash_execute" && args?.command) {
9817
+ const cwd = args.cwd ? ` (in ${args.cwd})` : "";
9818
+ const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
9819
+ preview = `$ ${args.command}${cwd}${timeout}`;
9435
9820
  }
9436
9821
  const response = await showPermissionPrompt(toolName, args, preview);
9437
9822
  if (response.action === "deny") {
@@ -9502,6 +9887,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
9502
9887
  glob_files: {},
9503
9888
  grep_search: {},
9504
9889
  delete_file: {},
9890
+ // Shell execution (always requires permission)
9891
+ bash_execute: {},
9505
9892
  // Server-side tools (executed via /api/ai/v1/tools Lambda)
9506
9893
  // Routed by ToolRouter to use B4M company API keys
9507
9894
  weather_info: {},
@@ -9567,6 +9954,7 @@ var DEFAULT_TOOL_CATEGORIES = {
9567
9954
  create_file: "prompt_always",
9568
9955
  delete_file: "prompt_always",
9569
9956
  shell_execute: "prompt_always",
9957
+ bash_execute: "prompt_always",
9570
9958
  git_commit: "prompt_always",
9571
9959
  git_push: "prompt_always",
9572
9960
  // ===== PROMPT DEFAULT (Repository read tools) =====
@@ -9728,10 +10116,122 @@ function getEnvironmentName(configApiConfig) {
9728
10116
  return "Bike4Mind";
9729
10117
  }
9730
10118
 
10119
+ // src/utils/contextLoader.ts
10120
+ import * as fs11 from "fs";
10121
+ import * as path13 from "path";
10122
+ import { homedir as homedir4 } from "os";
10123
+ var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
10124
+ var PROJECT_CONTEXT_FILES = [
10125
+ "CLAUDE.local.md",
10126
+ // gitignored, highest priority
10127
+ "CLAUDE.md",
10128
+ "AGENTS.md",
10129
+ // cross-tool standard
10130
+ "AI.local.md",
10131
+ // gitignored
10132
+ "AI.md",
10133
+ "INSTRUCTIONS.md"
10134
+ ];
10135
+ var GLOBAL_CONTEXT_FILES = ["AI.local.md", "AI.md"];
10136
+ function formatFileSize2(bytes) {
10137
+ if (bytes < 1024) return `${bytes}B`;
10138
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
10139
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
10140
+ }
10141
+ function tryReadContextFile(dir, filename, source) {
10142
+ const filePath = path13.join(dir, filename);
10143
+ try {
10144
+ const stats = fs11.lstatSync(filePath);
10145
+ if (stats.isDirectory()) {
10146
+ return null;
10147
+ }
10148
+ if (stats.isSymbolicLink()) {
10149
+ return {
10150
+ error: `${source === "global" ? "Global" : "Project"} ${filename} is a symlink (not allowed for security)`
10151
+ };
10152
+ }
10153
+ if (stats.size > CONTEXT_FILE_SIZE_LIMIT) {
10154
+ return {
10155
+ error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
10156
+ };
10157
+ }
10158
+ const content = fs11.readFileSync(filePath, "utf-8");
10159
+ return {
10160
+ filename,
10161
+ content,
10162
+ source,
10163
+ path: filePath
10164
+ };
10165
+ } catch (err) {
10166
+ if (err.code === "ENOENT") {
10167
+ return null;
10168
+ }
10169
+ if (err.code === "EACCES") {
10170
+ return {
10171
+ error: `Cannot read ${source} ${filename}: permission denied`
10172
+ };
10173
+ }
10174
+ return {
10175
+ error: `Cannot read ${source} ${filename}: ${err instanceof Error ? err.message : "Unknown error"}`
10176
+ };
10177
+ }
10178
+ }
10179
+ function findContextFile(dir, candidates, source) {
10180
+ for (const filename of candidates) {
10181
+ const result = tryReadContextFile(dir, filename, source);
10182
+ if (result === null) {
10183
+ continue;
10184
+ }
10185
+ if ("error" in result) {
10186
+ return { result: null, error: result.error };
10187
+ }
10188
+ return { result, error: null };
10189
+ }
10190
+ return { result: null, error: null };
10191
+ }
10192
+ function mergeContextContent(global, project) {
10193
+ if (global && project) {
10194
+ return `${global.content}
10195
+
10196
+ ---
10197
+
10198
+ ${project.content}`;
10199
+ }
10200
+ if (global) {
10201
+ return global.content;
10202
+ }
10203
+ if (project) {
10204
+ return project.content;
10205
+ }
10206
+ return "";
10207
+ }
10208
+ async function loadContextFiles(projectDir) {
10209
+ const errors = [];
10210
+ const globalDir = path13.join(homedir4(), ".bike4mind");
10211
+ const projectDirectory = projectDir || process.cwd();
10212
+ const [globalResult, projectResult] = await Promise.all([
10213
+ Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
10214
+ Promise.resolve(findContextFile(projectDirectory, PROJECT_CONTEXT_FILES, "project"))
10215
+ ]);
10216
+ if (globalResult.error) {
10217
+ errors.push(globalResult.error);
10218
+ }
10219
+ if (projectResult.error) {
10220
+ errors.push(projectResult.error);
10221
+ }
10222
+ const mergedContent = mergeContextContent(globalResult.result, projectResult.result);
10223
+ return {
10224
+ globalContext: globalResult.result,
10225
+ projectContext: projectResult.result,
10226
+ mergedContent,
10227
+ errors
10228
+ };
10229
+ }
10230
+
9731
10231
  // ../../b4m-core/packages/mcp/dist/src/client.js
9732
10232
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
9733
10233
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
9734
- import path12 from "path";
10234
+ import path14 from "path";
9735
10235
  import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
9736
10236
  var MCPClient = class {
9737
10237
  // Note: This class handles MCP server communication with repository filtering
@@ -9772,16 +10272,16 @@ var MCPClient = class {
9772
10272
  const root = process.env.INIT_CWD || process.cwd();
9773
10273
  const candidatePaths = [
9774
10274
  // When running from SST Lambda with node_modules structure (copyFiles)
9775
- path12.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
10275
+ path14.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
9776
10276
  // When running from SST Lambda deployed environment (/var/task)
9777
- path12.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10277
+ path14.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9778
10278
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
9779
- path12.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10279
+ path14.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9780
10280
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
9781
- path12.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10281
+ path14.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9782
10282
  // Original paths (backward compatibility)
9783
- path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
9784
- path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
10283
+ path14.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
10284
+ path14.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
9785
10285
  ];
9786
10286
  const serverScriptPath = candidatePaths.find((p) => existsSync8(p));
9787
10287
  if (!serverScriptPath) {
@@ -10585,7 +11085,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
10585
11085
  // package.json
10586
11086
  var package_default = {
10587
11087
  name: "@bike4mind/cli",
10588
- version: "0.2.8-feat-cli-session-resume.17148+30840381b",
11088
+ version: "0.2.10-dowgrade-mcp-sdk.17228+eeed70576",
10589
11089
  type: "module",
10590
11090
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
10591
11091
  license: "UNLICENSED",
@@ -10640,7 +11140,7 @@ var package_default = {
10640
11140
  "@google/generative-ai": "^0.21.0",
10641
11141
  "@joplin/turndown-plugin-gfm": "^1.0.62",
10642
11142
  "@mendable/firecrawl-js": "^1.29.3",
10643
- "@modelcontextprotocol/sdk": "^1.7.0",
11143
+ "@modelcontextprotocol/sdk": "1.20.1",
10644
11144
  "@octokit/rest": "^22.0.0",
10645
11145
  "@opensearch-project/opensearch": "2.11.0",
10646
11146
  "async-mutex": "^0.5.0",
@@ -10688,11 +11188,11 @@ var package_default = {
10688
11188
  },
10689
11189
  devDependencies: {
10690
11190
  "@bike4mind/agents": "0.1.0",
10691
- "@bike4mind/common": "2.38.2-feat-cli-session-resume.17148+30840381b",
10692
- "@bike4mind/mcp": "1.20.2-feat-cli-session-resume.17148+30840381b",
10693
- "@bike4mind/services": "2.33.2-feat-cli-session-resume.17148+30840381b",
10694
- "@bike4mind/utils": "2.1.3-feat-cli-session-resume.17148+30840381b",
10695
- "@types/better-sqlite3": "^7.6.12",
11191
+ "@bike4mind/common": "2.39.1-dowgrade-mcp-sdk.17228+eeed70576",
11192
+ "@bike4mind/mcp": "1.20.4-dowgrade-mcp-sdk.17228+eeed70576",
11193
+ "@bike4mind/services": "2.34.2-dowgrade-mcp-sdk.17228+eeed70576",
11194
+ "@bike4mind/utils": "2.1.4-dowgrade-mcp-sdk.17228+eeed70576",
11195
+ "@types/better-sqlite3": "^7.6.13",
10696
11196
  "@types/diff": "^5.0.9",
10697
11197
  "@types/jsonwebtoken": "^9.0.4",
10698
11198
  "@types/node": "^22.9.0",
@@ -10704,7 +11204,7 @@ var package_default = {
10704
11204
  typescript: "^5.9.3",
10705
11205
  vitest: "^3.2.4"
10706
11206
  },
10707
- gitHead: "30840381b429950872f0bb0c7eb7ea4408179cff"
11207
+ gitHead: "eeed70576ada327d03166880bbd1d1d32f7d4e7c"
10708
11208
  };
10709
11209
 
10710
11210
  // src/config/constants.ts
@@ -10956,6 +11456,24 @@ function CliApp() {
10956
11456
  }
10957
11457
  const allTools = [...b4mTools, ...mcpTools];
10958
11458
  console.log(`\u2705 Total: ${allTools.length} tool(s) available`);
11459
+ const projectDir = state.configStore.getProjectConfigDir();
11460
+ const contextResult = await loadContextFiles(projectDir);
11461
+ if (contextResult.globalContext) {
11462
+ console.log(`\u{1F4C4} Global context: ${contextResult.globalContext.filename}`);
11463
+ }
11464
+ if (contextResult.projectContext) {
11465
+ console.log(`\u{1F4C4} Project context: ${contextResult.projectContext.filename}`);
11466
+ }
11467
+ for (const error of contextResult.errors) {
11468
+ console.log(`\u26A0\uFE0F Context file error: ${error}`);
11469
+ }
11470
+ const contextSection = contextResult.mergedContent ? `
11471
+
11472
+ ## Project Context
11473
+
11474
+ Follow these project-specific instructions:
11475
+
11476
+ ${contextResult.mergedContent}` : "";
10959
11477
  const cliSystemPrompt = `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
10960
11478
 
10961
11479
  CORE BEHAVIOR:
@@ -10985,7 +11503,7 @@ EXAMPLES:
10985
11503
  - "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
10986
11504
  - "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
10987
11505
 
10988
- Remember: Use context from previous messages to understand follow-up questions.`;
11506
+ Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
10989
11507
  const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
10990
11508
  const agent = new ReActAgent({
10991
11509
  userId: config.userId,
@@ -11040,6 +11558,29 @@ Remember: Use context from previous messages to understand follow-up questions.`
11040
11558
  console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
11041
11559
  return;
11042
11560
  }
11561
+ const currentSteps = [];
11562
+ const stepHandler = (step) => {
11563
+ currentSteps.push(step);
11564
+ const currentSession = useCliStore.getState().session;
11565
+ if (currentSession) {
11566
+ const messages = [...currentSession.messages];
11567
+ const lastMessage = messages[messages.length - 1];
11568
+ if (lastMessage && lastMessage.role === "assistant") {
11569
+ messages[messages.length - 1] = {
11570
+ ...lastMessage,
11571
+ metadata: {
11572
+ ...lastMessage.metadata,
11573
+ steps: [...currentSteps]
11574
+ }
11575
+ };
11576
+ setStoreSession({
11577
+ ...currentSession,
11578
+ messages
11579
+ });
11580
+ }
11581
+ }
11582
+ };
11583
+ state.agent.on("action", stepHandler);
11043
11584
  try {
11044
11585
  let messageContent = message;
11045
11586
  let userMessageContent = message;
@@ -11053,6 +11594,21 @@ Remember: Use context from previous messages to understand follow-up questions.`
11053
11594
  content: userMessageContent,
11054
11595
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
11055
11596
  };
11597
+ const pendingAssistantMessage = {
11598
+ role: "assistant",
11599
+ content: "...",
11600
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11601
+ metadata: {
11602
+ steps: []
11603
+ }
11604
+ };
11605
+ const sessionWithMessages = {
11606
+ ...state.session,
11607
+ messages: [...state.session.messages, userMessage, pendingAssistantMessage],
11608
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
11609
+ };
11610
+ setState((prev) => ({ ...prev, session: sessionWithMessages }));
11611
+ setStoreSession(sessionWithMessages);
11056
11612
  const recentMessages = state.session.messages.slice(-20);
11057
11613
  const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
11058
11614
  role: msg.role,
@@ -11066,26 +11622,28 @@ Remember: Use context from previous messages to understand follow-up questions.`
11066
11622
  console.log("\n\u26A0\uFE0F Action denied by user\n");
11067
11623
  }
11068
11624
  const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
11069
- const assistantMessage = {
11625
+ const currentSession = useCliStore.getState().session;
11626
+ if (!currentSession) return;
11627
+ const finalMessages = [...currentSession.messages];
11628
+ const finalAssistantMessage = {
11070
11629
  role: "assistant",
11071
11630
  content: result.finalAnswer,
11072
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11631
+ timestamp: pendingAssistantMessage.timestamp,
11073
11632
  metadata: {
11074
11633
  tokenUsage: {
11075
11634
  prompt: 0,
11076
- // TODO: Extract from result
11077
11635
  completion: 0,
11078
11636
  total: result.completionInfo.totalTokens
11079
11637
  },
11080
11638
  steps: result.steps,
11081
11639
  // Complete history: thoughts, actions, observations
11082
11640
  permissionDenied
11083
- // Mark if this ended due to permission denial
11084
11641
  }
11085
11642
  };
11643
+ finalMessages[finalMessages.length - 1] = finalAssistantMessage;
11086
11644
  const updatedSession = {
11087
- ...state.session,
11088
- messages: [...state.session.messages, userMessage, assistantMessage],
11645
+ ...currentSession,
11646
+ messages: finalMessages,
11089
11647
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
11090
11648
  metadata: {
11091
11649
  ...state.session.metadata,
@@ -11105,6 +11663,8 @@ Remember: Use context from previous messages to understand follow-up questions.`
11105
11663
  }
11106
11664
  }
11107
11665
  console.error("Error processing message:", error);
11666
+ } finally {
11667
+ state.agent.off("action", stepHandler);
11108
11668
  }
11109
11669
  };
11110
11670
  const handleImageDetected = async (imageData) => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CurationArtifactType
4
- } from "./chunk-H6SHASMZ.js";
4
+ } from "./chunk-DJPXSSP4.js";
5
5
  import "./chunk-PDX44BCA.js";
6
6
 
7
7
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/llmMarkdownGenerator.js
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CurationArtifactType
4
- } from "./chunk-H6SHASMZ.js";
4
+ } from "./chunk-DJPXSSP4.js";
5
5
  import "./chunk-PDX44BCA.js";
6
6
 
7
7
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/markdownGenerator.js
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  findMostSimilarMemento,
4
4
  getRelevantMementos
5
- } from "./chunk-3CCBDIFR.js";
6
- import "./chunk-DNSBC3LO.js";
5
+ } from "./chunk-GO75FMLY.js";
6
+ import "./chunk-GCVIRGIN.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
- import "./chunk-H6SHASMZ.js";
8
+ import "./chunk-DJPXSSP4.js";
9
9
  import "./chunk-PDX44BCA.js";
10
10
  export {
11
11
  findMostSimilarMemento,
@@ -120,7 +120,7 @@ import {
120
120
  validateMermaidSyntax,
121
121
  warmUpSettingsCache,
122
122
  withRetry
123
- } from "./chunk-DNSBC3LO.js";
123
+ } from "./chunk-GCVIRGIN.js";
124
124
  import {
125
125
  Logger,
126
126
  NotificationDeduplicator,
@@ -129,7 +129,7 @@ import {
129
129
  postLowCreditsNotificationToSlack,
130
130
  postMessageToSlack
131
131
  } from "./chunk-AMDXHL6S.js";
132
- import "./chunk-H6SHASMZ.js";
132
+ import "./chunk-DJPXSSP4.js";
133
133
  import "./chunk-PDX44BCA.js";
134
134
  export {
135
135
  AWSBackend,
@@ -186,6 +186,7 @@ import {
186
186
  SessionEvents,
187
187
  SettingKeySchema,
188
188
  SettingTypeSchema,
189
+ SlackEvents,
189
190
  SpeechToTextModels,
190
191
  SpiderCompleteAction,
191
192
  SpiderErrorAction,
@@ -292,7 +293,7 @@ import {
292
293
  validateQuestMasterArtifactV2,
293
294
  validateReactArtifactV2,
294
295
  validateSvgArtifactV2
295
- } from "./chunk-H6SHASMZ.js";
296
+ } from "./chunk-DJPXSSP4.js";
296
297
  import "./chunk-PDX44BCA.js";
297
298
  export {
298
299
  ALL_IMAGE_MODELS,
@@ -481,6 +482,7 @@ export {
481
482
  SessionEvents,
482
483
  SettingKeySchema,
483
484
  SettingTypeSchema,
485
+ SlackEvents,
484
486
  SpeechToTextModels,
485
487
  SpiderCompleteAction,
486
488
  SpiderErrorAction,
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  SubtractCreditsSchema,
4
4
  subtractCredits
5
- } from "./chunk-LIRY24M5.js";
6
- import "./chunk-DNSBC3LO.js";
5
+ } from "./chunk-VZU4Z7WI.js";
6
+ import "./chunk-GCVIRGIN.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
- import "./chunk-H6SHASMZ.js";
8
+ import "./chunk-DJPXSSP4.js";
9
9
  import "./chunk-PDX44BCA.js";
10
10
  export {
11
11
  SubtractCreditsSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.2.8-feat-cli-session-resume.17148+30840381b",
3
+ "version": "0.2.10-dowgrade-mcp-sdk.17228+eeed70576",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -55,7 +55,7 @@
55
55
  "@google/generative-ai": "^0.21.0",
56
56
  "@joplin/turndown-plugin-gfm": "^1.0.62",
57
57
  "@mendable/firecrawl-js": "^1.29.3",
58
- "@modelcontextprotocol/sdk": "^1.7.0",
58
+ "@modelcontextprotocol/sdk": "1.20.1",
59
59
  "@octokit/rest": "^22.0.0",
60
60
  "@opensearch-project/opensearch": "2.11.0",
61
61
  "async-mutex": "^0.5.0",
@@ -103,11 +103,11 @@
103
103
  },
104
104
  "devDependencies": {
105
105
  "@bike4mind/agents": "0.1.0",
106
- "@bike4mind/common": "2.38.2-feat-cli-session-resume.17148+30840381b",
107
- "@bike4mind/mcp": "1.20.2-feat-cli-session-resume.17148+30840381b",
108
- "@bike4mind/services": "2.33.2-feat-cli-session-resume.17148+30840381b",
109
- "@bike4mind/utils": "2.1.3-feat-cli-session-resume.17148+30840381b",
110
- "@types/better-sqlite3": "^7.6.12",
106
+ "@bike4mind/common": "2.39.1-dowgrade-mcp-sdk.17228+eeed70576",
107
+ "@bike4mind/mcp": "1.20.4-dowgrade-mcp-sdk.17228+eeed70576",
108
+ "@bike4mind/services": "2.34.2-dowgrade-mcp-sdk.17228+eeed70576",
109
+ "@bike4mind/utils": "2.1.4-dowgrade-mcp-sdk.17228+eeed70576",
110
+ "@types/better-sqlite3": "^7.6.13",
111
111
  "@types/diff": "^5.0.9",
112
112
  "@types/jsonwebtoken": "^9.0.4",
113
113
  "@types/node": "^22.9.0",
@@ -119,5 +119,5 @@
119
119
  "typescript": "^5.9.3",
120
120
  "vitest": "^3.2.4"
121
121
  },
122
- "gitHead": "30840381b429950872f0bb0c7eb7ea4408179cff"
122
+ "gitHead": "eeed70576ada327d03166880bbd1d1d32f7d4e7c"
123
123
  }